From 646bc03724929ccffb9f287612eb8e40acd8f03b Mon Sep 17 00:00:00 2001 From: Daniel-Aaron-Bloom <76709210+Daniel-Aaron-Bloom@users.noreply.github.com> Date: Wed, 23 Apr 2025 08:47:04 -0700 Subject: [PATCH 001/112] ci: add `workflow_dispatch` (#11) --- .github/workflows/solana.yml | 1 + .github/workflows/universal-rs.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 9b8f3696e..37107374d 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -1,6 +1,7 @@ name: solana on: + workflow_dispatch: push: branches: - main diff --git a/.github/workflows/universal-rs.yml b/.github/workflows/universal-rs.yml index 37ce603b8..75ca17eb4 100644 --- a/.github/workflows/universal-rs.yml +++ b/.github/workflows/universal-rs.yml @@ -1,6 +1,7 @@ name: universal-rs on: + workflow_dispatch: push: branches: - main @@ -73,4 +74,4 @@ jobs: toolchain: ${{ env.RUSTC_VERSION }} components: rustfmt - run: cargo fmt --all --check - working-directory: ./universal/rs \ No newline at end of file + working-directory: ./universal/rs From 939ffada1d3c2be16bdcb459fb185cf9b93c136c Mon Sep 17 00:00:00 2001 From: A5 Pickle <5342825+a5-pickle@users.noreply.github.com> Date: Mon, 28 Apr 2025 09:56:10 -0700 Subject: [PATCH 002/112] solana: fix idl build (#16) * solana: fix idl build workflows: remove rustup default * solana: update Makefile --------- Co-authored-by: A5 Pickle --- .github/workflows/solana.yml | 18 +++++++++--------- solana/Makefile | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 37107374d..8309254df 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -59,9 +59,9 @@ jobs: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - - name: Set default Rust toolchain - run: rustup default stable - working-directory: ./solana + # - name: Set default Rust toolchain + # run: rustup default stable + # working-directory: ./solana - name: make check-idl run: make check-idl working-directory: ./solana @@ -80,9 +80,9 @@ jobs: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - - name: Set default Rust toolchain - run: rustup default stable - working-directory: ./solana + # - name: Set default Rust toolchain + # run: rustup default stable + # working-directory: ./solana - name: make anchor-test run: make anchor-test working-directory: ./solana @@ -101,9 +101,9 @@ jobs: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - - name: Set default Rust toolchain - run: rustup default stable - working-directory: ./solana + # - name: Set default Rust toolchain + # run: rustup default stable + # working-directory: ./solana - name: make anchor-test-upgrade run: make anchor-test-upgrade working-directory: ./solana diff --git a/solana/Makefile b/solana/Makefile index 0d72bd4fe..1def5a753 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -7,6 +7,12 @@ CLONED_MAINNET_PROGRAMS=\ ts/tests/artifacts/mainnet_cctp_token_messenger_minter.so \ ts/tests/artifacts/mainnet_cctp_message_transmitter.so +PROGRAM_NAMES=matching_engine token_router upgrade_manager + +### Building the IDL requires a nightly build. We arbitrarily chose the same +### date as the release of Anchor 0.30.1. +IDL_TOOLCHAIN=nightly-2024-06-20 + .PHONY: all all: check @@ -42,11 +48,15 @@ endif .PHONY: anchor-test-setup anchor-test-setup: node_modules ts/tests/artifacts $(CLONED_MAINNET_PROGRAMS) - anchor build -- --features integration-test + anchor build --no-idl -- --features integration-test .PHONY: idl idl: - anchor build -- --features localnet + mkdir -p target/idl target/types + for program in $(PROGRAM_NAMES); do \ + RUSTUP_TOOLCHAIN=$(IDL_TOOLCHAIN) anchor idl build -p $$program -o target/idl/$$program.json; \ + anchor idl type -o target/types/$$program.ts target/idl/$$program.json; \ + done mkdir -p ts/src/idl/json mkdir -p ts/src/idl/ts cp -r target/idl/* ts/src/idl/json/ @@ -65,7 +75,7 @@ anchor-test: anchor-test-setup .PHONY: anchor-test-upgrade anchor-test-upgrade: node_modules ts/tests/artifacts $(CLONED_MAINNET_PROGRAMS) - anchor build -- --features testnet + anchor build --no-idl -- --features testnet cp target/deploy/matching_engine.so ts/tests/artifacts/new_testnet_matching_engine.so cp target/deploy/token_router.so ts/tests/artifacts/new_testnet_token_router.so cp target/deploy/upgrade_manager.so ts/tests/artifacts/testnet_upgrade_manager.so From 25d7866999a509c51c11bb2f900146c6af58a9c6 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 14 Feb 2025 17:35:31 +0000 Subject: [PATCH 003/112] make offer_token mutable account --- .../matching-engine/src/processor/auction/offer/improve.rs | 1 + .../src/processor/auction/offer/place_initial/cctp.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs index b99dc4d7d..c2c3c6ee6 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs @@ -42,6 +42,7 @@ pub struct ImproveOffer<'info> { active_auction: ActiveAuction<'info>, #[account( + mut, constraint = { offer_token.key() != active_auction.custody_token.key() } @ MatchingEngineError::InvalidOfferToken, diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs index fab8c53ba..44f301b0f 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs @@ -93,6 +93,7 @@ pub struct PlaceInitialOfferCctp<'info> { )] auction: Box>, + #[account(mut)] offer_token: Box>, #[account( From a66b63cbc139ff015f5e580a6350c2ea280ace8d Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 5 Feb 2025 15:47:32 +0000 Subject: [PATCH 004/112] [broken] starting off the work to add integration test Started off integration test work --- solana/Cargo.lock | 5712 ++++++++++++++--- solana/programs/matching-engine/Cargo.toml | 10 +- solana/programs/matching-engine/src/lib.rs | 1 + .../src/processor/admin/initialize.rs | 2 +- .../tests/fixtures/upgrade_manager.so | Bin 0 -> 334608 bytes .../tests/fixtures/usdc_mint.json | 14 + .../tests/fixtures/usdc_mint_devnet.json | 14 + .../tests/initialize_integration_tests.rs | 302 + ...bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json | 1 + .../matching-engine/tests/utils/airdrop.rs | 44 + .../matching-engine/tests/utils/constants.rs | 79 + .../matching-engine/tests/utils/mint.rs | 78 + .../matching-engine/tests/utils/mod.rs | 8 + .../tests/utils/token_account.rs | 166 + .../tests/utils/upgrade_manager.rs | 40 + 15 files changed, 5598 insertions(+), 873 deletions(-) create mode 100755 solana/programs/matching-engine/tests/fixtures/upgrade_manager.so create mode 100644 solana/programs/matching-engine/tests/fixtures/usdc_mint.json create mode 100644 solana/programs/matching-engine/tests/fixtures/usdc_mint_devnet.json create mode 100644 solana/programs/matching-engine/tests/initialize_integration_tests.rs create mode 100644 solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json create mode 100644 solana/programs/matching-engine/tests/utils/airdrop.rs create mode 100644 solana/programs/matching-engine/tests/utils/constants.rs create mode 100644 solana/programs/matching-engine/tests/utils/mint.rs create mode 100644 solana/programs/matching-engine/tests/utils/mod.rs create mode 100644 solana/programs/matching-engine/tests/utils/token_account.rs create mode 100644 solana/programs/matching-engine/tests/utils/upgrade_manager.rs diff --git a/solana/Cargo.lock b/solana/Cargo.lock index e45ef5212..e0a31a5ac 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.4.3" @@ -56,6 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom 0.2.11", "once_cell", "version_check", "zerocopy", @@ -70,6 +96,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anchor-attribute-access-control" version = "0.30.1" @@ -139,7 +186,7 @@ dependencies = [ "anchor-syn", "anyhow", "bs58 0.5.0", - "heck", + "heck 0.3.3", "proc-macro2", "quote", "serde_json", @@ -215,7 +262,7 @@ checksum = "31cf97b4e6f7d6144a05e435660fcf757dbc3446d38d0e2b851d11ed13625bba" dependencies = [ "anchor-lang-idl-spec", "anyhow", - "heck", + "heck 0.3.3", "regex", "serde", "serde_json", @@ -239,12 +286,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04bd077c34449319a1e4e0bc21cea572960c9ae0d0fefda0dd7c52fcc3c647a3" dependencies = [ "anchor-lang", - "spl-associated-token-account", - "spl-pod", + "spl-associated-token-account 3.0.2", + "spl-pod 0.2.2", "spl-token", - "spl-token-2022", - "spl-token-group-interface", - "spl-token-metadata-interface", + "spl-token-2022 3.0.2", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", ] [[package]] @@ -256,7 +303,7 @@ dependencies = [ "anyhow", "bs58 0.5.0", "cargo_toml", - "heck", + "heck 0.3.3", "proc-macro2", "quote", "serde", @@ -266,12 +313,50 @@ dependencies = [ "thiserror", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "aquamarine" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" +dependencies = [ + "include_dir", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-bn254" version = "0.4.0" @@ -313,7 +398,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "paste", "rustc_version", @@ -336,7 +421,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", "proc-macro2", "quote", @@ -365,7 +450,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.4", ] [[package]] @@ -401,19 +486,109 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "assert_matches" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -424,18 +599,45 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "base64" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "1.3.3" @@ -574,7 +776,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", "syn_derive", ] @@ -622,6 +824,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.4.0" @@ -670,7 +893,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] @@ -679,6 +902,43 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror", +] + [[package]] name = "cargo_toml" version = "0.19.2" @@ -691,12 +951,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -717,7 +978,22 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", ] [[package]] @@ -729,6 +1005,81 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width 0.1.14", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.16.1", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -749,12 +1100,34 @@ dependencies = [ "web-sys", ] +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "constant_time_eq" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -764,6 +1137,24 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -858,8 +1249,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.48", + "strsim 0.10.0", + "syn 2.0.58", ] [[package]] @@ -870,26 +1261,96 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] -name = "derivation-path" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" - -[[package]] -name = "derivative" -version = "2.2.0" +name = "dashmap" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", ] +[[package]] +name = "data-encoding" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.4", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -910,6 +1371,61 @@ dependencies = [ "subtle", ] +[[package]] +name = "dir-diff" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" +dependencies = [ + "walkdir", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + [[package]] name = "ed25519" version = "1.5.3" @@ -945,12 +1461,72 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint 0.4.4", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -971,1448 +1547,4176 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "feature-probe" -version = "0.1.1" +name = "errno" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "generic-array" -version = "0.14.7" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "serde", - "typenum", - "version_check", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "getrandom" -version = "0.1.16" +name = "feature-probe" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] -name = "getrandom" -version = "0.2.11" +name = "filetime" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", - "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", + "libredox", + "windows-sys 0.59.0", ] [[package]] -name = "hashbrown" -version = "0.11.2" +name = "flate2" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ - "ahash 0.7.8", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "hashbrown" -version = "0.13.2" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ - "ahash 0.8.11", + "num-traits", ] [[package]] -name = "hashbrown" -version = "0.14.3" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "heck" -version = "0.3.3" +name = "form_urlencoded" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "unicode-segmentation", + "percent-encoding", ] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "fragile" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ - "libc", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "hex" -version = "0.4.3" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] [[package]] -name = "hex-literal" -version = "0.4.1" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "hmac" -version = "0.8.1" +name = "futures-executor" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "hmac" -version = "0.12.1" +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "hmac-drbg" -version = "0.3.0" +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "humantime" -version = "2.1.0" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] -name = "ident_case" -version = "1.0.1" +name = "futures-task" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] -name = "im" -version = "15.1.0" +name = "futures-util" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "bitmaps", - "rand_core 0.6.4", - "rand_xoshiro", - "rayon", - "serde", - "sized-chunks", - "typenum", - "version_check", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "indexmap" -version = "2.1.0" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "equivalent", - "hashbrown 0.14.3", + "serde", + "typenum", + "version_check", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "gethostname" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" dependencies = [ - "either", + "libc", + "winapi", ] [[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "jobserver" -version = "0.1.27" +name = "getrandom" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ + "cfg-if", + "js-sys", "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] -name = "js-sys" -version = "0.3.68" +name = "getrandom" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "getrandom" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ - "cpufeatures", + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.152" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] -name = "libsecp256k1" -version = "0.6.0" +name = "goblin" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" dependencies = [ - "arrayref", - "base64 0.12.3", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "typenum", + "log", + "plain", + "scroll", ] [[package]] -name = "libsecp256k1-core" -version = "0.2.2" +name = "h2" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util 0.7.13", + "tracing", ] [[package]] -name = "libsecp256k1-gen-ecmult" +name = "hash32" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" dependencies = [ - "libsecp256k1-core", + "byteorder", ] [[package]] -name = "libsecp256k1-gen-genmult" -version = "0.2.1" +name = "hashbrown" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "libsecp256k1-core", + "ahash 0.7.8", ] [[package]] -name = "light-poseidon" -version = "0.2.0" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ark-bn254", - "ark-ff", - "num-bigint", - "thiserror", + "ahash 0.7.8", ] [[package]] -name = "liquidity-layer-common-solana" -version = "0.0.0" +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "anchor-lang", - "cfg-if", - "liquidity-layer-messages", - "solana-program", - "wormhole-cctp-solana", - "wormhole-solana-consts", + "ahash 0.8.11", ] [[package]] -name = "liquidity-layer-messages" -version = "0.0.0" -dependencies = [ - "wormhole-io", - "wormhole-raw-vaas", -] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] -name = "lock_api" -version = "0.4.11" +name = "heck" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ - "autocfg", - "scopeguard", + "unicode-segmentation", ] [[package]] -name = "log" -version = "0.4.20" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "matching-engine" -version = "0.0.0" +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "anchor-lang", - "anchor-spl", - "cfg-if", - "hex", - "hex-literal", - "liquidity-layer-common-solana", - "ruint", - "solana-program", - "wormhole-solana-utils", + "libc", ] [[package]] -name = "memchr" -version = "2.7.1" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] -name = "memmap2" -version = "0.5.10" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ - "libc", + "crypto-mac", + "digest 0.9.0", ] [[package]] -name = "memoffset" -version = "0.9.0" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "autocfg", + "digest 0.10.7", ] [[package]] -name = "merlin" -version = "3.0.0" +name = "hmac-drbg" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", ] [[package]] -name = "num-bigint" -version = "0.4.4" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "num-derive" -version = "0.3.3" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "bytes", + "http", + "pin-project-lite", ] [[package]] -name = "num-derive" -version = "0.4.2" +name = "httparse" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "hyper-rustls" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ - "autocfg", - "num-traits", + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", ] [[package]] -name = "num-traits" -version = "0.2.17" +name = "iana-time-zone" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ - "autocfg", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "num_enum" -version = "0.6.1" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "num_enum_derive 0.6.1", + "cc", ] [[package]] -name = "num_enum" -version = "0.7.2" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "num_enum_derive 0.7.2", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "num_enum_derive" -version = "0.6.1" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 2.0.48", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "num_enum_derive" -version = "0.7.2" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2", - "quote", - "syn 2.0.48", + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "icu_locid_transform_data" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "icu_normalizer" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "icu_normalizer_data" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] -name = "parking_lot_core" -version = "0.9.9" +name = "icu_properties" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", ] [[package]] -name = "paste" -version = "1.0.14" +name = "icu_properties_data" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] -name = "pbkdf2" -version = "0.4.0" +name = "icu_provider" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ - "crypto-mac", + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "pbkdf2" -version = "0.11.0" +name = "icu_provider_macros" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ - "digest 0.10.7", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "polyval" -version = "0.5.3" +name = "idna" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "toml 0.5.11", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "im" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", ] [[package]] -name = "proc-macro-crate" -version = "3.1.0" +name = "include_dir" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ - "toml_edit 0.21.1", + "include_dir_macros", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "include_dir_macros" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "index_list" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] +checksum = "fa38453685e5fe724fd23ff6c1a158c1e2ca21ce0c2718fa11e96e70e99fd4de" [[package]] -name = "proc-macro2" -version = "1.0.78" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "unicode-ident", + "autocfg", + "hashbrown 0.12.3", ] [[package]] -name = "qstring" -version = "0.7.2" +name = "indexmap" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ - "percent-encoding", + "equivalent", + "hashbrown 0.14.3", ] [[package]] -name = "qualifier_attr" -version = "0.2.2" +name = "indicatif" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "console", + "number_prefix", + "portable-atomic", + "unicode-width 0.2.0", + "web-time", ] [[package]] -name = "quote" -version = "1.0.35" +name = "ipnet" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "rand" -version = "0.7.3" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", + "either", ] [[package]] -name = "rand" -version = "0.8.5" +name = "itoa" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "js-sys" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "wasm-bindgen", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "jsonrpc-core" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "keccak" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ - "getrandom 0.1.16", + "cpufeatures", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.11", -] +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "rand_hc" -version = "0.2.0" +name = "libc" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] -name = "rand_xoshiro" -version = "0.6.0" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "rand_core 0.6.4", + "bitflags 2.4.2", + "libc", + "redox_syscall 0.5.8", ] [[package]] -name = "rayon" -version = "1.8.0" +name = "libsecp256k1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" dependencies = [ - "either", - "rayon-core", + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", ] [[package]] -name = "rayon-core" -version = "1.12.0" +name = "libsecp256k1-core" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "crunchy", + "digest 0.9.0", + "subtle", ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" dependencies = [ - "bitflags 1.3.2", + "libsecp256k1-core", ] [[package]] -name = "regex" -version = "1.10.2" +name = "libsecp256k1-gen-genmult" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "libsecp256k1-core", ] [[package]] -name = "regex-automata" -version = "0.4.3" +name = "light-poseidon" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "ark-bn254", + "ark-ff", + "num-bigint 0.4.4", + "thiserror", ] [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "ruint" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e1574d439643c8962edf612a888e7cc5581bcdf36cb64e6bc88466b03b2daa" +name = "liquidity-layer-common-solana" +version = "0.0.0" dependencies = [ - "ruint-macro", - "thiserror", + "anchor-lang", + "cfg-if", + "liquidity-layer-messages", + "solana-program", + "wormhole-cctp-solana", + "wormhole-solana-consts", ] [[package]] -name = "ruint-macro" -version = "1.1.0" +name = "liquidity-layer-messages" +version = "0.0.0" +dependencies = [ + "wormhole-io", + "wormhole-raw-vaas", +] + +[[package]] +name = "litemap" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "lock_api" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] -name = "rustc_version" -version = "0.4.0" +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" dependencies = [ - "semver", + "hashbrown 0.12.3", ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "lz4" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" +dependencies = [ + "lz4-sys", +] [[package]] -name = "ryu" -version = "1.0.16" +name = "lz4-sys" +version = "1.11.1+lz4-1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" +dependencies = [ + "cc", + "libc", +] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "matching-engine" +version = "0.0.0" +dependencies = [ + "anchor-lang", + "anchor-spl", + "bincode", + "cfg-if", + "hex", + "hex-literal", + "liquidity-layer-common-solana", + "ruint", + "serde_json", + "solana-program", + "solana-program-test", + "solana-sdk", + "wormhole-solana-utils", +] + +[[package]] +name = "memchr" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] -name = "semver" -version = "1.0.21" +name = "memmap2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] [[package]] -name = "serde" -version = "1.0.195" +name = "memoffset" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ - "serde_derive", + "autocfg", ] [[package]] -name = "serde_bytes" -version = "0.11.14" +name = "memoffset" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ - "serde", + "autocfg", ] [[package]] -name = "serde_derive" -version = "1.0.195" +name = "merlin" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", ] [[package]] -name = "serde_json" -version = "1.0.111" +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ - "itoa", - "ryu", - "serde", + "adler2", ] [[package]] -name = "serde_spanned" -version = "0.6.5" +name = "mio" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "serde", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", ] [[package]] -name = "serde_with" -version = "2.3.3" +name = "mockall" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ - "serde", - "serde_with_macros", + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", ] [[package]] -name = "serde_with_macros" -version = "2.3.3" +name = "mockall_derive" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ - "darling", + "cfg-if", "proc-macro2", "quote", - "syn 2.0.48", + "syn 1.0.109", ] [[package]] -name = "sha2" -version = "0.9.9" +name = "modular-bitfield" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "modular-bitfield-impl", + "static_assertions", ] [[package]] -name = "sha2" -version = "0.10.8" +name = "modular-bitfield-impl" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "sha3" -version = "0.9.1" +name = "nix" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", ] [[package]] -name = "sha3" -version = "0.10.8" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "digest 0.10.7", - "keccak", + "memchr", + "minimal-lexical", ] [[package]] -name = "signature" -version = "1.6.4" +name = "normalize-line-endings" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "siphasher" -version = "0.3.11" +name = "num" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] [[package]] -name = "sized-chunks" -version = "0.6.5" +name = "num-bigint" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "bitmaps", - "typenum", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "smallvec" -version = "1.11.2" +name = "num-bigint" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] [[package]] -name = "solana-frozen-abi" -version = "1.18.15" +name = "num-complex" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c00a6aca244dfa904e2c4a26406ba7b0987344ceaec932f3cda0b35eff0babc" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ - "block-buffer 0.10.4", - "bs58 0.4.0", - "bv", - "either", - "generic-array", - "im", - "lazy_static", - "log", - "memmap2", - "rustc_version", - "serde", - "serde_bytes", - "serde_derive", - "sha2 0.10.8", - "solana-frozen-abi-macro", - "subtle", - "thiserror", + "autocfg", + "num-traits", ] [[package]] -name = "solana-frozen-abi-macro" -version = "1.18.15" +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bed58b27b9b8877893f69bc5cfd1c62e984315e0229d83cf8a32ad0933c0d6c9" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "rustc_version", - "syn 2.0.48", + "syn 1.0.109", ] [[package]] -name = "solana-logger" -version = "1.18.15" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2daf61ae582edf9634adf8e5021faf002df0d3f69078ecbcd6c7b41bdf833" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "env_logger", - "lazy_static", - "log", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "solana-program" -version = "1.18.15" +name = "num-integer" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4908f360900d0a1aa81c7bad7937c78f0825c3f08ff0b22f1de0e43e5946f2" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-serialize", + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive 0.7.2", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "opentelemetry" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "js-sys", + "lazy_static", + "percent-encoding", + "pin-project", + "rand 0.8.5", + "thiserror", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "percentage" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num", +] + +[[package]] +name = "pin-project" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml 0.5.11", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "qualifier_attr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls", + "rustls-native-certs", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.4.2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util 0.7.13", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.11", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ruint" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e1574d439643c8962edf612a888e7cc5581bcdf36cb64e6bc88466b03b2daa" +dependencies = [ + "ruint-macro", + "thiserror", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "seqlock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c67b6f14ecc5b86c66fa63d76b5092352678545a8a3cdae80aef5128371910" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "solana-account-decoder" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b109fd3a106e079005167e5b0e6f6d2c88bbedec32530837b584791a8b5abf36" +dependencies = [ + "Inflector", + "base64 0.21.7", + "bincode", + "bs58 0.4.0", + "bv", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-config-program", + "solana-sdk", + "spl-token", + "spl-token-2022 1.0.0", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-accounts-db" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9829d10d521f3ed5e50c12d2b62784e2901aa484a92c2aa3924151da046139" +dependencies = [ + "arrayref", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "flate2", + "fnv", + "im", + "index_list", + "itertools", + "lazy_static", + "log", + "lz4", + "memmap2", + "modular-bitfield", + "num-derive 0.4.2", + "num-traits", + "num_cpus", + "num_enum 0.7.2", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "seqlock", + "serde", + "serde_derive", + "smallvec", + "solana-bucket-map", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-nohash-hasher", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", + "static_assertions", + "strum", + "strum_macros", + "tar", + "tempfile", + "thiserror", +] + +[[package]] +name = "solana-address-lookup-table-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3527a26138b5deb126f13c27743f3d95ac533abee5979e4113f6d59ef919cc6" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive 0.4.2", + "num-traits", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-banks-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58fa66e1e240097665e7f87b267aa8e976ea3fcbd86918c8fd218c875395ada" +dependencies = [ + "borsh 1.5.0", + "futures", + "solana-banks-interface", + "solana-program", + "solana-sdk", + "tarpc", + "thiserror", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-banks-interface" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54d0a4334c153eadaa0326296a47a92d110c1cc975075fd6e1a7b67067f9812" +dependencies = [ + "serde", + "solana-sdk", + "tarpc", +] + +[[package]] +name = "solana-banks-server" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cbe287a0f859362de9b155fabd44e479eba26d5d80e07a7d021297b7b06ecba" +dependencies = [ + "bincode", + "crossbeam-channel", + "futures", + "solana-accounts-db", + "solana-banks-interface", + "solana-client", + "solana-runtime", + "solana-sdk", + "solana-send-transaction-service", + "tarpc", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-bpf-loader-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8cc27ceda9a22804d73902f5d718ff1331aa53990c2665c90535f6b182db259" +dependencies = [ + "bincode", + "byteorder", + "libsecp256k1", + "log", + "scopeguard", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-bucket-map" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca55ec9b8d01d2e3bba9fad77b27c9a8fd51fe12475549b93a853d921b653139" +dependencies = [ + "bv", + "bytemuck", + "log", + "memmap2", + "modular-bitfield", + "num_enum 0.7.2", + "rand 0.8.5", + "solana-measure", + "solana-sdk", + "tempfile", +] + +[[package]] +name = "solana-clap-utils" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074ef478856a45d5627270fbc6b331f91de9aae7128242d9e423931013fb8a2a" +dependencies = [ + "chrono", + "clap 2.34.0", + "rpassword", + "solana-remote-wallet", + "solana-sdk", + "thiserror", + "tiny-bip39", + "uriparse", + "url", +] + +[[package]] +name = "solana-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a9f32c42402c4b9484d5868ac74b7e0a746e3905d8bfd756e1203e50cbb87e" +dependencies = [ + "async-trait", + "bincode", + "dashmap", + "futures", + "futures-util", + "indexmap 2.1.0", + "indicatif", + "log", + "quinn", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-quic-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-rpc-client-nonce-utils", + "solana-sdk", + "solana-streamer", + "solana-thin-client", + "solana-tpu-client", + "solana-udp-client", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-compute-budget-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af050a6e0b402e322aa21f5441c7e27cdd52624a2d659f455b68afd7cda218c" +dependencies = [ + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-config-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d75b803860c0098e021a26f0624129007c15badd5b0bc2fbd9f0e1a73060d3b" +dependencies = [ + "bincode", + "chrono", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-connection-cache" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9306ede13e8ceeab8a096bcf5fa7126731e44c201ca1721ea3c38d89bcd4111" +dependencies = [ + "async-trait", + "bincode", + "crossbeam-channel", + "futures-util", + "indexmap 2.1.0", + "log", + "rand 0.8.5", + "rayon", + "rcgen", + "solana-measure", + "solana-metrics", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-cost-model" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c852790063f7646a1c5199234cc82e1304b55a3b3fb8055a0b5c8b0393565c1c" +dependencies = [ + "lazy_static", + "log", + "rustc_version", + "solana-address-lookup-table-program", + "solana-bpf-loader-program", + "solana-compute-budget-program", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-loader-v4-program", + "solana-metrics", + "solana-program-runtime", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", +] + +[[package]] +name = "solana-frozen-abi" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ab2c30c15311b511c0d1151e4ab6bc9a3e080a37e7c6e7c2d96f5784cf9434" +dependencies = [ + "block-buffer 0.10.4", + "bs58 0.4.0", + "bv", + "either", + "generic-array", + "im", + "lazy_static", + "log", + "memmap2", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "sha2 0.10.8", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c142f779c3633ac83c84d04ff06c70e1f558c876f13358bed77ba629c7417932" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.58", +] + +[[package]] +name = "solana-loader-v4-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b58f70f5883b0f26a6011ed23f76c493a3f22df63aec46cfe8e1b9bf82b5cc" +dependencies = [ + "log", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana_rbpf", +] + +[[package]] +name = "solana-logger" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121d36ffb3c6b958763312cbc697fbccba46ee837d3a0aa4fc0e90fcb3b884f3" +dependencies = [ + "env_logger", + "lazy_static", + "log", +] + +[[package]] +name = "solana-measure" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c01a7f9cdc9d9d37a3d5651b2fe7ec9d433c2a3470b9f35897e373b421f0737" +dependencies = [ + "log", + "solana-sdk", +] + +[[package]] +name = "solana-metrics" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e36052aff6be1536bdf6f737c6e69aca9dbb6a2f3f582e14ecb0ddc0cd66ce" +dependencies = [ + "crossbeam-channel", + "gethostname", + "lazy_static", + "log", + "reqwest", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-net-utils" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1f5c6be9c5b272866673741e1ebc64b2ea2118e5c6301babbce526fdfb15f4" +dependencies = [ + "bincode", + "clap 3.2.25", + "crossbeam-channel", + "log", + "nix", + "rand 0.8.5", + "serde", + "serde_derive", + "socket2", + "solana-logger", + "solana-sdk", + "solana-version", + "tokio", + "url", +] + +[[package]] +name = "solana-nohash-hasher" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8a731ed60e89177c8a7ab05fe0f1511cedd3e70e773f288f9de33a9cfdc21e" + +[[package]] +name = "solana-perf" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28acaf22477566a0fbddd67249ea5d859b39bacdb624aff3fadd3c5745e2643c" +dependencies = [ + "ahash 0.8.11", + "bincode", + "bv", + "caps", + "curve25519-dalek", + "dlopen2", + "fnv", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "rayon", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c10f4588cefd716b24a1a40dd32c278e43a560ab8ce4de6b5805c9d113afdfa1" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "base64 0.21.7", + "bincode", + "bitflags 2.4.2", + "blake3", + "borsh 0.10.3", + "borsh 0.9.3", + "borsh 1.5.0", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.11", + "itertools", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "light-poseidon", + "log", + "memoffset 0.9.0", + "num-bigint 0.4.4", + "num-derive 0.4.2", + "num-traits", + "parking_lot", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-program-runtime" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf0c3eab2a80f514289af1f422c121defb030937643c43b117959d6f1932fb5" +dependencies = [ + "base64 0.21.7", + "bincode", + "eager", + "enum-iterator", + "itertools", + "libc", + "log", + "num-derive 0.4.2", + "num-traits", + "percentage", + "rand 0.8.5", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-program-test" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1382a5768ff738e283770ee331d0a4fa04aa1aceed8eb820a97094c93d53b72" +dependencies = [ + "assert_matches", + "async-trait", + "base64 0.21.7", + "bincode", + "chrono-humanize", + "crossbeam-channel", + "log", + "serde", + "solana-accounts-db", + "solana-banks-client", + "solana-banks-interface", + "solana-banks-server", + "solana-bpf-loader-program", + "solana-logger", + "solana-program-runtime", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "solana_rbpf", + "test-case", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-pubsub-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b064e76909d33821b80fdd826e6757251934a52958220c92639f634bea90366d" +dependencies = [ + "crossbeam-channel", + "futures-util", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-quic-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a90e40ee593f6e9ddd722d296df56743514ae804975a76d47e7afed4e3da244" +dependencies = [ + "async-mutex", + "async-trait", + "futures", + "itertools", + "lazy_static", + "log", + "quinn", + "quinn-proto", + "rcgen", + "rustls", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-rpc-client-api", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66468f9c014992167de10cc68aad6ac8919a8c8ff428dc88c0d2b4da8c02b8b7" +dependencies = [ + "lazy_static", + "num_cpus", +] + +[[package]] +name = "solana-remote-wallet" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c191019f4d4f84281a6d0dd9a43181146b33019627fc394e42e08ade8976b431" +dependencies = [ + "console", + "dialoguer", + "log", + "num-derive 0.4.2", + "num-traits", + "parking_lot", + "qstring", + "semver", + "solana-sdk", + "thiserror", + "uriparse", +] + +[[package]] +name = "solana-rpc-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ed4628e338077c195ddbf790693d410123d17dec0a319b5accb4aaee3fb15c" +dependencies = [ + "async-trait", + "base64 0.21.7", + "bincode", + "bs58 0.4.0", + "indicatif", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "solana-vote-program", + "tokio", +] + +[[package]] +name = "solana-rpc-client-api" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c913551faa4a1ae4bbfef6af19f3a5cf847285c05b4409e37c8993b3444229" +dependencies = [ + "base64 0.21.7", + "bs58 0.4.0", + "jsonrpc-core", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "solana-rpc-client-nonce-utils" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a47b6bb1834e6141a799db62bbdcf80d17a7d58d7bc1684c614e01a7293d7cf" +dependencies = [ + "clap 2.34.0", + "solana-clap-utils", + "solana-rpc-client", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-runtime" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73a12e1270121e1ca6a4e86d6d0f5c339f0811a8435161d9eee54cbb0a083859" +dependencies = [ + "aquamarine", + "arrayref", + "base64 0.21.7", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "dir-diff", + "flate2", + "fnv", + "im", + "index_list", + "itertools", + "lazy_static", + "log", + "lru", + "lz4", + "memmap2", + "mockall", + "modular-bitfield", + "num-derive 0.4.2", + "num-traits", + "num_cpus", + "num_enum 0.7.2", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "serde", + "serde_derive", + "serde_json", + "solana-accounts-db", + "solana-address-lookup-table-program", + "solana-bpf-loader-program", + "solana-bucket-map", + "solana-compute-budget-program", + "solana-config-program", + "solana-cost-model", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-loader-v4-program", + "solana-measure", + "solana-metrics", + "solana-perf", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-version", + "solana-vote", + "solana-vote-program", + "solana-zk-token-proof-program", + "solana-zk-token-sdk", + "static_assertions", + "strum", + "strum_macros", + "symlink", + "tar", + "tempfile", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-sdk" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "580ad66c2f7a4c3cb3244fe21440546bd500f5ecb955ad9826e92a78dded8009" +dependencies = [ + "assert_matches", + "base64 0.21.7", + "bincode", + "bitflags 2.4.2", + "borsh 1.5.0", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.2", + "pbkdf2 0.11.0", + "qstring", + "qualifier_attr", + "rand 0.7.3", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_with", + "sha2 0.10.8", + "sha3 0.10.8", + "siphasher", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b75d0f193a27719257af19144fdaebec0415d1c9e9226ae4bd29b791be5e9bd" +dependencies = [ + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-send-transaction-service" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3218f670f582126a3859c4fd152e922b93b3748a636bb143f970391925723577" +dependencies = [ + "crossbeam-channel", + "log", + "solana-client", + "solana-measure", + "solana-metrics", + "solana-runtime", + "solana-sdk", + "solana-tpu-client", +] + +[[package]] +name = "solana-stake-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb3e0d2dc7080b9fa61b34699b176911684f5e04e8df4b565b2b6c962bb4321" +dependencies = [ + "bincode", + "log", + "rustc_version", + "solana-config-program", + "solana-program-runtime", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-streamer" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8476e41ad94fe492e8c06697ee35912cf3080aae0c9e9ac6430835256ccf056" +dependencies = [ + "async-channel", + "bytes", + "crossbeam-channel", + "futures-util", + "histogram", + "indexmap 2.1.0", + "itertools", + "libc", + "log", + "nix", + "pem", + "percentage", + "pkcs8", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rcgen", + "rustls", + "smallvec", + "solana-metrics", + "solana-perf", + "solana-sdk", + "thiserror", + "tokio", + "x509-parser", +] + +[[package]] +name = "solana-system-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f31e04f5baad7cbc2281fea312c4e48277da42a93a0ba050b74edc5a74d63c" +dependencies = [ + "bincode", + "log", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-thin-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c02245d0d232430e79dc0d624aa42d50006097c3aec99ac82ac299eaa3a73f" +dependencies = [ + "bincode", + "log", + "rayon", + "solana-connection-cache", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", +] + +[[package]] +name = "solana-tpu-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67251506ed03de15f1347b46636b45c47da6be75015b4a13f0620b21beb00566" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap 2.1.0", + "indicatif", + "log", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-transaction-status" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3d36db1b2ab2801afd5482aad9fb15ed7959f774c81a77299fdd0ddcf839d4" +dependencies = [ + "Inflector", + "base64 0.21.7", + "bincode", + "borsh 0.10.3", + "bs58 0.4.0", + "lazy_static", + "log", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-sdk", + "spl-associated-token-account 2.3.0", + "spl-memo", + "spl-token", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "solana-udp-client" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a754a3c2265eb02e0c35aeaca96643951f03cee6b376afe12e0cf8860ffccd1" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-net-utils", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-version" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f44776bd685cc02e67ba264384acc12ef2931d01d1a9f851cb8cdbd3ce455b9e" +dependencies = [ + "log", + "rustc_version", + "semver", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", +] + +[[package]] +name = "solana-vote" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5983370c95b615dc5f5d0e85414c499f05380393c578749bcd14c114c77c9bc" +dependencies = [ + "crossbeam-channel", + "itertools", + "log", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", + "solana-vote-program", + "thiserror", +] + +[[package]] +name = "solana-vote-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25810970c91feb579bd3f67dca215fce971522e42bfd59696af89c5dfebd997c" +dependencies = [ + "bincode", + "log", + "num-derive 0.4.2", + "num-traits", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-zk-token-proof-program" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be1c15d4aace575e2de73ebeb9b37bac455e89bee9a8c3531f47ac5066b33e1" +dependencies = [ + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cbdf4249b6dfcbba7d84e2b53313698043f60f8e22ce48286e6fbe8a17c8d16" +dependencies = [ + "aes-gcm-siv", "base64 0.21.7", "bincode", - "bitflags 2.4.2", - "blake3", - "borsh 0.10.3", - "borsh 0.9.3", - "borsh 1.5.0", - "bs58 0.4.0", - "bv", "bytemuck", - "cc", - "console_error_panic_hook", - "console_log", + "byteorder", "curve25519-dalek", - "getrandom 0.2.11", + "getrandom 0.1.16", "itertools", - "js-sys", "lazy_static", - "libc", - "libsecp256k1", - "light-poseidon", - "log", - "memoffset", - "num-bigint", + "merlin", "num-derive 0.4.2", "num-traits", - "parking_lot", - "rand 0.8.5", - "rustc_version", - "rustversion", + "rand 0.7.3", "serde", - "serde_bytes", - "serde_derive", "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "solana_rbpf" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5d083187e3b3f453e140f292c09186881da8a02a7b5e27f645ee26de3d9cc5" +dependencies = [ + "byteorder", + "combine", + "goblin", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "scroll", + "thiserror", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "spl-associated-token-account" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" +dependencies = [ + "assert_matches", + "borsh 0.10.3", + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "spl-associated-token-account" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" +dependencies = [ + "assert_matches", + "borsh 1.5.0", + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022 3.0.2", + "thiserror", +] + +[[package]] +name = "spl-discriminator" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.1.2", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d1814406e98b08c5cd02c1126f83fd407ad084adce0b05fda5730677822eac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.2.0", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" +dependencies = [ + "quote", + "spl-discriminator-syn 0.1.2", + "syn 2.0.58", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn 0.2.0", + "syn 2.0.58", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", + "thiserror", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-pod" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" +dependencies = [ + "borsh 0.10.3", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-pod" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" +dependencies = [ + "borsh 1.5.0", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.4.1", +] + +[[package]] +name = "spl-program-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" +dependencies = [ + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.3.2", + "thiserror", +] + +[[package]] +name = "spl-program-error" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49065093ea91f57b9b2bd81493ff705e2ad4e64507a07dbc02b085778e02770e" +dependencies = [ + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.4.1", + "thiserror", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", "sha2 0.10.8", - "sha3 0.10.8", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-sdk-macro", - "thiserror", - "tiny-bip39", - "wasm-bindgen", - "zeroize", + "syn 2.0.58", ] [[package]] -name = "solana-sdk" -version = "1.18.15" +name = "spl-tlv-account-resolution" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50ec330850953d4971b052ff98c74a8e67e7618b4aed9f4971b8d3b68fcd1cd" +checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" dependencies = [ - "assert_matches", - "base64 0.21.7", - "bincode", - "bitflags 2.4.2", - "borsh 1.5.0", - "bs58 0.4.0", "bytemuck", - "byteorder", - "chrono", - "derivation-path", - "digest 0.10.7", - "ed25519-dalek", - "ed25519-dalek-bip32", - "generic-array", - "hmac 0.12.1", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1", - "log", - "memmap2", - "num-derive 0.4.2", - "num-traits", - "num_enum 0.7.2", - "pbkdf2 0.11.0", - "qstring", - "qualifier_attr", - "rand 0.7.3", - "rand 0.8.5", - "rustc_version", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "serde_json", - "serde_with", - "sha2 0.10.8", - "sha3 0.10.8", - "siphasher", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", "solana-program", - "solana-sdk-macro", - "thiserror", - "uriparse", - "wasm-bindgen", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] -name = "solana-sdk-macro" -version = "1.18.15" +name = "spl-tlv-account-resolution" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ef2ea49002d1bf52a4a8509570b2c3b88e7b6d0a131b11bbd637ca1e1df0ff" +checksum = "cace91ba08984a41556efe49cbf2edca4db2f577b649da7827d3621161784bf8" dependencies = [ - "bs58 0.4.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.48", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", ] [[package]] -name = "solana-security-txt" -version = "1.1.1" +name = "spl-token" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "solana-program", + "thiserror", +] [[package]] -name = "solana-zk-token-sdk" -version = "1.18.15" +name = "spl-token-2022" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cafb3df56516086f65e2a08a8cd03f504236f3b5348299abd45415d1d18ba32" +checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" dependencies = [ - "aes-gcm-siv", - "base64 0.21.7", - "bincode", + "arrayref", "bytemuck", - "byteorder", - "curve25519-dalek", - "getrandom 0.1.16", - "itertools", - "lazy_static", - "merlin", "num-derive 0.4.2", "num-traits", - "rand 0.7.3", - "serde", - "serde_json", - "sha3 0.9.1", + "num_enum 0.7.2", "solana-program", - "solana-sdk", - "subtle", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.4.1", + "spl-type-length-value 0.3.0", "thiserror", - "zeroize", ] [[package]] -name = "spl-associated-token-account" +name = "spl-token-2022" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" +checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" dependencies = [ - "assert_matches", - "borsh 1.5.0", + "arrayref", + "bytemuck", "num-derive 0.4.2", "num-traits", + "num_enum 0.7.2", "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.2.2", "spl-token", - "spl-token-2022", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", + "spl-transfer-hook-interface 0.6.3", + "spl-type-length-value 0.4.3", "thiserror", ] [[package]] -name = "spl-discriminator" -version = "0.2.2" +name = "spl-token-group-interface" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d1814406e98b08c5cd02c1126f83fd407ad084adce0b05fda5730677822eac" +checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator-derive", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", ] [[package]] -name = "spl-discriminator-derive" +name = "spl-token-group-interface" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d419b5cfa3ee8e0f2386fd7e02a33b3ec8a7db4a9c7064a2ea24849dc4a273b6" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", +] + +[[package]] +name = "spl-token-metadata-interface" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" +dependencies = [ + "borsh 0.10.3", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" +dependencies = [ + "borsh 1.5.0", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aabdb7c471566f6ddcee724beb8618449ea24b399e58d464d6b5bc7db550259" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.5.1", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a98359769cd988f7b35c02558daa56d496a7e3bd8626e61f90a7c757eedb9b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-tlv-account-resolution 0.6.3", + "spl-type-length-value 0.4.3", +] + +[[package]] +name = "spl-type-length-value" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ce13429dbd41d2cee8a73931c05fda0b0c8ca156a8b0c19445642550bb61a" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ + "heck 0.4.1", + "proc-macro2", "quote", - "spl-discriminator-syn", - "syn 2.0.48", + "rustversion", + "syn 1.0.109", ] [[package]] -name = "spl-discriminator-syn" -version = "0.2.0" +name = "subtle" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "sha2 0.10.8", - "syn 2.0.48", - "thiserror", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "spl-memo" -version = "4.0.0" +name = "syn_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ - "solana-program", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "spl-pod" -version = "0.2.2" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" -dependencies = [ - "borsh 1.5.0", - "bytemuck", - "solana-program", - "solana-zk-token-sdk", - "spl-program-error", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "spl-program-error" -version = "0.4.1" +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49065093ea91f57b9b2bd81493ff705e2ad4e64507a07dbc02b085778e02770e" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "num-derive 0.4.2", - "num-traits", - "solana-program", - "spl-program-error-derive", - "thiserror", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", ] [[package]] -name = "spl-program-error-derive" -version = "0.4.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "sha2 0.10.8", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] -name = "spl-tlv-account-resolution" -version = "0.6.3" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cace91ba08984a41556efe49cbf2edca4db2f577b649da7827d3621161784bf8" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "spl-token" -version = "4.0.0" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "arrayref", - "bytemuck", - "num-derive 0.3.3", - "num-traits", - "num_enum 0.6.1", - "solana-program", - "thiserror", + "core-foundation-sys", + "libc", ] [[package]] -name = "spl-token-2022" -version = "3.0.2" +name = "tar" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ - "arrayref", - "bytemuck", - "num-derive 0.4.2", - "num-traits", - "num_enum 0.7.2", - "solana-program", - "solana-security-txt", - "solana-zk-token-sdk", - "spl-memo", - "spl-pod", - "spl-token", - "spl-token-group-interface", - "spl-token-metadata-interface", - "spl-transfer-hook-interface", - "spl-type-length-value", - "thiserror", + "filetime", + "libc", + "xattr", ] [[package]] -name = "spl-token-group-interface" -version = "0.2.3" +name = "tarpc" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419b5cfa3ee8e0f2386fd7e02a33b3ec8a7db4a9c7064a2ea24849dc4a273b6" +checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" dependencies = [ - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "anyhow", + "fnv", + "futures", + "humantime", + "opentelemetry", + "pin-project", + "rand 0.8.5", + "serde", + "static_assertions", + "tarpc-plugins", + "thiserror", + "tokio", + "tokio-serde", + "tokio-util 0.6.10", + "tracing", + "tracing-opentelemetry", ] [[package]] -name = "spl-token-metadata-interface" -version = "0.3.3" +name = "tarpc-plugins" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" +checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "borsh 1.5.0", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "spl-transfer-hook-interface" -version = "0.6.3" +name = "tempfile" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a98359769cd988f7b35c02558daa56d496a7e3bd8626e61f90a7c757eedb9b" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ - "arrayref", - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-tlv-account-resolution", - "spl-type-length-value", + "cfg-if", + "fastrand", + "getrandom 0.3.1", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] -name = "spl-type-length-value" -version = "0.4.3" +name = "termcolor" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ce13429dbd41d2cee8a73931c05fda0b0c8ca156a8b0c19445642550bb61a" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "winapi-util", ] [[package]] -name = "strsim" -version = "0.10.0" +name = "termtree" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] -name = "subtle" -version = "2.4.1" +name = "test-case" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] [[package]] -name = "syn" -version = "1.0.109" +name = "test-case-core" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ + "cfg-if", "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.58", ] [[package]] -name = "syn" -version = "2.0.48" +name = "test-case-macros" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.58", + "test-case-core", ] [[package]] -name = "syn_derive" -version = "0.1.8" +name = "textwrap" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.48", + "unicode-width 0.1.14", ] [[package]] -name = "termcolor" -version = "1.4.0" +name = "textwrap" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" @@ -2431,7 +5735,48 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", ] [[package]] @@ -2453,6 +5798,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2493,6 +5848,115 @@ dependencies = [ "wormhole-io", ] +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.25.4", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -2529,7 +5993,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.1.0", "toml_datetime", "winnow 0.5.33", ] @@ -2540,7 +6004,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap", + "indexmap 2.1.0", "toml_datetime", "winnow 0.5.33", ] @@ -2551,11 +6015,101 @@ version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.7", + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.7", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "sha1", + "thiserror", + "url", + "utf-8", + "webpki-roots 0.24.0", ] [[package]] @@ -2585,6 +6139,24 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.4.1" @@ -2595,6 +6167,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "upgrade-manager" version = "0.0.0" @@ -2620,12 +6213,78 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2638,6 +6297,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.91" @@ -2659,10 +6327,22 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.91" @@ -2681,7 +6361,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2702,6 +6382,31 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -2733,19 +6438,71 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2754,42 +6511,90 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.5.33" @@ -2808,6 +6613,25 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.4.2", +] + [[package]] name = "wormhole-cctp-solana" version = "0.3.0-alpha.0" @@ -2871,6 +6695,80 @@ dependencies = [ "wormhole-solana-consts", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "xattr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -2888,7 +6786,28 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "synstructure 0.13.1", ] [[package]] @@ -2908,5 +6827,56 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", ] diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 0704b5873..bb7c3057d 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -42,6 +42,14 @@ cfg-if.workspace = true [dev-dependencies] hex-literal.workspace = true +solana-program-test = "1.18.15" +solana-sdk = "1.18.15" +serde_json = "1.0.138" +bincode = "1.3.3" +solana-cli-output = "1.18.15" +base64 = "0.22.1" +lazy_static = "1.4.0" +bs58 = "0.5.0" [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 58b353535..07f92f66f 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -8,6 +8,7 @@ mod error; mod events; mod processor; +pub use processor::InitializeArgs; use processor::*; pub mod state; diff --git a/solana/programs/matching-engine/src/processor/admin/initialize.rs b/solana/programs/matching-engine/src/processor/admin/initialize.rs index 7ae99e18b..bba3d4d9c 100644 --- a/solana/programs/matching-engine/src/processor/admin/initialize.rs +++ b/solana/programs/matching-engine/src/processor/admin/initialize.rs @@ -106,7 +106,7 @@ pub struct Initialize<'info> { } #[derive(Debug, AnchorSerialize, AnchorDeserialize)] pub struct InitializeArgs { - auction_params: AuctionParameters, + pub auction_params: AuctionParameters, } pub fn initialize(ctx: Context, args: InitializeArgs) -> Result<()> { diff --git a/solana/programs/matching-engine/tests/fixtures/upgrade_manager.so b/solana/programs/matching-engine/tests/fixtures/upgrade_manager.so new file mode 100755 index 0000000000000000000000000000000000000000..e675b885e094e3afa36e01c7ad4e60ebec74b2b1 GIT binary patch literal 334608 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
  • _td#-&wP4Pdp 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 le;, +} + +impl Solver { + pub fn new(keypair: Rc, token_account: Option, endpoint: Option) -> Self { + Self { actor: TestingActor::new(keypair, token_account), endpoint } + } + + pub fn get_endpoint(&self) -> Option { + self.endpoint.clone() + } + + pub fn keypair(&self) -> Rc { + self.actor.keypair.clone() + } + + pub fn pubkey(&self) -> Pubkey { + self.actor.keypair.pubkey() + } +} + +pub struct TestingActor { + pub keypair: Rc, + pub token_account: Option, +} + +impl std::fmt::Debug for TestingActor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", self.keypair.pubkey(), self.token_account) + } +} + +impl TestingActor { + pub fn new(keypair: Rc, token_account: Option) -> Self { + Self { keypair, token_account } + } + pub fn pubkey(&self) -> Pubkey { + self.keypair.pubkey() + } + pub fn keypair(&self) -> Rc { + self.keypair.clone() + } + + pub fn token_account_address(&self) -> Option { + self.token_account.as_ref().map(|t| t.address) + } +} + +pub struct TestingActors { + pub owner: TestingActor, + pub owner_assistant: TestingActor, + pub fee_recipient: TestingActor, + pub relayer: TestingActor, + pub solvers: Vec, + pub liquidator: TestingActor, +} + +impl TestingActors { + pub fn new() -> Self { + let owner_kp = Rc::new(read_keypair_from_file(OWNER_KEYPAIR_PATH)); + let owner = TestingActor::new(owner_kp.clone(), None); + let owner_assistant = TestingActor::new(owner_kp.clone(), None); + let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None); + let relayer = TestingActor::new(Rc::new(Keypair::new()), None); + // TODO: Change player 1 solver to use the keyfile + let mut solvers = vec![]; + solvers.extend(vec![ + Solver::new(Rc::new(Keypair::new()), None, None), + Solver::new(Rc::new(Keypair::new()), None, None), + Solver::new(Rc::new(Keypair::new()), None, None), + ]); + let liquidator = TestingActor::new(Rc::new(Keypair::new()), None); + Self { owner, owner_assistant, fee_recipient, relayer, solvers, liquidator } + } + + pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { + let mut actors = Vec::new(); + actors.push(&mut self.fee_recipient); + for solver in &mut self.solvers { + actors.push(&mut solver.actor); + } + actors.push(&mut self.liquidator); + actors + } + + /// Transfer Lamports to Executors + async fn airdrop_all(&self, test_context: &Rc>) { + airdrop(test_context, &self.owner.pubkey(), 10000000000).await; + airdrop(test_context, &self.owner_assistant.pubkey(), 10000000000).await; + airdrop(test_context, &self.fee_recipient.pubkey(), 10000000000).await; + airdrop(test_context, &self.relayer.pubkey(), 10000000000).await; + for solver in self.solvers.iter() { + airdrop(test_context, &solver.pubkey(), 10000000000).await; + } + airdrop(test_context, &self.liquidator.pubkey(), 10000000000).await; + } + + /// Set up ATAs for Various Owners + async fn create_atas(&mut self, test_context: &Rc>) { + for actor in self.token_account_actors() { + let usdc_ata = create_token_account(test_context.clone(), &actor.keypair(), &USDC_MINT_ADDRESS).await; + actor.token_account = Some(usdc_ata); + } + } +} + +pub struct TestingContext { + pub program_data_account: Pubkey, + pub testing_actors: TestingActors, + pub test_context: Rc>, +} + +pub async fn setup_test_context() -> TestingContext { + let mut program_test = ProgramTest::new( + "matching_engine", // Replace with your program name + PROGRAM_ID, + None, + ); + program_test.set_compute_max_units(1000000000); + program_test.set_transaction_account_lock_limit(1000); + + // Setup Testing Actors + let mut testing_actors = TestingActors::new(); + + // Initialise Upgrade Manager + let program_data = initialise_upgrade_manager(&mut program_test, &PROGRAM_ID, testing_actors.owner.pubkey()); + + // Start and get test context + let test_context = Rc::new(RefCell::new(program_test.start_with_context().await)); + + // Airdrop to all actors + testing_actors.airdrop_all(&test_context).await; + + // Create USDC mint + let _mint_fixture = MintFixture::new_from_file(&test_context, USDC_MINT_FIXTURE_PATH); + + // Create USDC ATAs for all actors that need them + testing_actors.create_atas(&test_context).await; + + TestingContext { program_data_account: program_data, testing_actors, test_context } +} + +pub struct InitializeFixture { + pub test_context: Rc>, + pub custodian: Custodian, +} + +async fn initialize_program(testing_context: &TestingContext) -> InitializeFixture { + let test_context = testing_context.test_context.clone(); + + let (custodian, _custodian_bump) = Pubkey::find_program_address( + &[Custodian::SEED_PREFIX], + &PROGRAM_ID, + ); + + let (auction_config, _auction_config_bump) = Pubkey::find_program_address( + &[ + AuctionConfig::SEED_PREFIX, + &0u32.to_be_bytes(), + ], + &PROGRAM_ID, + ); + + // Create AuctionParameters + let auction_params = matching_engine::state::AuctionParameters { + user_penalty_reward_bps: 250_000, // 25% + initial_penalty_bps: 250_000, // 25% + duration: 2, + grace_period: 5, + penalty_period: 10, + min_offer_delta_bps: 20_000, // 2% + security_deposit_base: 4_200_000, + security_deposit_bps: 5_000, // 0.5% + }; + + // Create the instruction data + let ix_data = matching_engine::instruction::Initialize { + args: InitializeArgs { + auction_params, + }, + }; + + // Get account metas + let accounts = Initialize { + owner: testing_context.testing_actors.owner.pubkey(), + custodian, + auction_config, + owner_assistant: testing_context.testing_actors.owner_assistant.pubkey(), + fee_recipient: testing_context.testing_actors.fee_recipient.pubkey(), + fee_recipient_token: testing_context.testing_actors.fee_recipient.token_account_address().unwrap(), + cctp_mint_recipient: CCTP_MINT_RECIPIENT, + usdc: matching_engine::accounts::Usdc{mint: USDC_MINT_ADDRESS}, + program_data: testing_context.program_data_account, + upgrade_manager_authority: common::UPGRADE_MANAGER_AUTHORITY, + upgrade_manager_program: common::UPGRADE_MANAGER_PROGRAM_ID, + bpf_loader_upgradeable_program: bpf_loader_upgradeable::id(), + system_program: system_program::id(), + token_program: spl_token::id(), + associated_token_program: spl_associated_token_account::id(), + }; + + // Create the instruction + let instruction = Instruction { + program_id: PROGRAM_ID, + accounts: accounts.to_account_metas(None), + data: ix_data.data(), + }; + + // Create and sign transaction + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&test_context.borrow().payer.pubkey()), + ); + transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], test_context.borrow().last_blockhash); + + // Process transaction + test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + + // Verify the results + let custodian_account = test_context.borrow_mut().banks_client + .get_account(custodian) + .await + .unwrap() + .unwrap(); + + let custodian_data = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + + InitializeFixture { test_context, custodian: custodian_data } +} + +#[tokio::test] +pub async fn test_initialize_program() { + + let testing_context = setup_test_context().await; + + let initialize_fixture = initialize_program(&testing_context).await; + + let custodian_data = initialize_fixture.custodian; + + // Verify owner is correct + assert_eq!(custodian_data.owner, testing_context.testing_actors.owner.pubkey()); + // Verify owner assistant is correct + assert_eq!(custodian_data.owner_assistant, testing_context.testing_actors.owner_assistant.pubkey()); + // Verify fee recipient token is correct + assert_eq!(custodian_data.fee_recipient_token, testing_context.testing_actors.fee_recipient.token_account.unwrap().address); + // Verify auction config id is 0 + assert_eq!(custodian_data.auction_config_id, 0); + // Verify next proposal id is 0 + assert_eq!(custodian_data.next_proposal_id, 0); +} + diff --git a/solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json b/solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json new file mode 100644 index 000000000..3e28fb443 --- /dev/null +++ b/solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json @@ -0,0 +1 @@ +[112,55,233,99,229,91,68,85,207,63,10,46,103,0,49,250,22,189,30,167,157,146,26,148,175,155,212,104,86,182,185,192,12,26,88,134,254,16,147,223,159,196,56,194,150,249,247,39,91,119,24,182,188,14,21,109,141,51,108,88,240,131,153,109] \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs new file mode 100644 index 000000000..8cf7e2c42 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -0,0 +1,44 @@ +use solana_program_test::ProgramTestContext; +use std::rc::Rc; +use std::cell::RefCell; +use solana_sdk::{ + pubkey::Pubkey, + system_instruction, + signature::Signer, +}; +use solana_sdk::transaction::Transaction; + + +/// Airdrops SOL to a given recipient +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `recipient` - The recipient of the airdrop +/// * `amount` - The amount of SOL to airdrop + +pub async fn airdrop( + test_context: &Rc>, + recipient: &Pubkey, + amount: u64, +) { + println!("Airdropping {:?} with amount {:?}", recipient, amount); + let mut ctx = test_context.borrow_mut(); + + // Create the transfer instruction with values from the context + let transfer_ix = system_instruction::transfer( + &ctx.payer.pubkey(), + recipient, + amount, + ); + + // Create and send transaction + let tx = Transaction::new_signed_with_payer( + &[transfer_ix.clone()], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await.unwrap(); +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs new file mode 100644 index 000000000..8645464c9 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -0,0 +1,79 @@ +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; +use std::str::FromStr; + +// Program IDs +pub const CORE_BRIDGE_PID: Pubkey = Pubkey::from_str("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth").unwrap(); +pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); + +/// Keypairs as base64 strings (taken from consts.ts in ts tests) +// pub const PAYER_KEYPAIR_B64: &str = "cDfpY+VbRFXPPwouZwAx+ha9HqedkhqUr5vUaFa2ucAMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQ=="; +// pub const OWNER_ASSISTANT_KEYPAIR_B64: &str = "900mlHo1RRdhxUKuBnnPowQ7yqb4rJ1dC7K1PM+pRxeuCWamoSkQdY+3hXAeX0OBXanyqg4oyBl8g1z1sDnSWg=="; +// pub const OWNER_KEYPAIR_B64: &str = "t0zuiHtsaDJBSUFzkvXNttgXOMvZy0bbuUPGEByIJEHAUdFeBdSAesMbgbuH1v/y+B8CdTSkCIZZNuCntHQ+Ig=="; +// pub const PLAYER_ONE_KEYPAIR_B64: &str = "4STrqllKVVva0Fphqyf++6uGTVReATBe2cI26oIuVBft77CQP9qQrMTU1nM9ql0EnCpSgmCmm20m8khMo9WdPQ=="; + +/// Keypairs as base58 strings (taken from consts.ts in ts tests using a converter) +pub const PAYER_KEYPAIR_B58: &str = "4NMwxzmYj2uvHuq8xoqhY8RXg0Pd5zkvmfWAL6YvbYFuViXVCBDK5Pru9GgqEVEZo6UXcPVH6rdR8JKgKxHGkXDp"; +pub const OWNER_ASSISTANT_KEYPAIR_B58: &str = "2UbUgoidcNHxVEDG6ADNKGaGDqBTXTVw6B9pWvJtLNhbxcQDkdeEyBYBYYYxxDy92ckXUEaU9chWEGi5jc8Uc9e3"; +pub const OWNER_KEYPAIR_B58: &str = "3M5rkG5DQVEGQFRtA1qruxPqJvYBbkGCdkCdB9ZjcnQnYL9ec8W78pLcQHVtjJzHP8phUXQ8V1SXbgZK9ZaFaS6U"; +pub const PLAYER_ONE_KEYPAIR_B58: &str = "yqJrKqGqzuW6nEmfj62AgvZWqgGv9TqxfvPXiGvf8DxGDWz3UNkQdDfKDnBYpHQxPRVrYMupDKqbGVYHhfZApGb"; + +// Helper functions to get keypairs +pub fn get_payer_keypair() -> Keypair { + Keypair::from_base58_string(PAYER_KEYPAIR_B58) +} + +pub fn get_owner_assistant_keypair() -> Keypair { + Keypair::from_base58_string(OWNER_ASSISTANT_KEYPAIR_B58) +} + +pub fn get_owner_keypair() -> Keypair { + Keypair::from_base58_string(OWNER_KEYPAIR_B58) +} + +pub fn get_player_one_keypair() -> Keypair { + Keypair::from_base58_string(PLAYER_ONE_KEYPAIR_B58) +} + +// Other constants +pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111111111111111111111111115"); +pub const GUARDIAN_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; +pub const USDC_MINT_ADDRESS: Pubkey = solana_program::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); +pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + +// Chain to Domain mapping +pub const CHAIN_TO_DOMAIN: &[(Chain, u32)] = &[ + (Chain::Ethereum, 0), + (Chain::Avalanche, 1), + (Chain::Optimism, 2), + (Chain::Arbitrum, 3), + (Chain::Solana, 5), + (Chain::Base, 6), + (Chain::Polygon, 7), +]; + +// Enum for Chain types +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Chain { + Ethereum, + Avalanche, + Optimism, + Arbitrum, + Solana, + Base, + Polygon, +} + +// Registered Token Routers +lazy_static::lazy_static! { + pub static ref REGISTERED_TOKEN_ROUTERS: std::collections::HashMap> = { + let mut m = std::collections::HashMap::new(); + m.insert(Chain::Ethereum, vec![0xf0; 32]); + m.insert(Chain::Avalanche, vec![0xf1; 32]); + m.insert(Chain::Optimism, vec![0xf2; 32]); + m.insert(Chain::Arbitrum, vec![0xf3; 32]); + m.insert(Chain::Base, vec![0xf6; 32]); + m.insert(Chain::Polygon, vec![0xf7; 32]); + m + }; +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mint.rs b/solana/programs/matching-engine/tests/utils/mint.rs new file mode 100644 index 000000000..011bcf4de --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/mint.rs @@ -0,0 +1,78 @@ +use solana_sdk::{ + account::{AccountSharedData, ReadableAccount, WritableAccount}, + program_pack::Pack, + signer::Signer, + pubkey::Pubkey, +}; +use solana_program_test::ProgramTestContext; +use solana_cli_output::CliAccount; +use anchor_spl::token::spl_token; +use spl_token::state::Mint; + +use std::{cell::RefCell, fs::File, io::Read, path::PathBuf, rc::Rc, str::FromStr}; + +#[derive(Clone)] +pub struct MintFixture { + pub test_ctx: Rc>, + pub key: Pubkey, + pub mint: spl_token::state::Mint, + pub token_program: Pubkey, +} + +impl MintFixture { + + /// Creates a new MintFixture from a file + /// + /// # Arguments + /// + /// * `ctx` - The test context + /// * `relative_path` - The relative path to the mint file + /// + /// # Returns + /// + /// A new MintFixture + pub fn new_from_file( + ctx: &Rc>, + relative_path: &str, + ) -> MintFixture { + let ctx_ref = Rc::clone(ctx); + + let (address, account_info) = { + let mut ctx = ctx.borrow_mut(); + + // load cargo workspace path from env + let mut path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); + path.push(relative_path); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let address = Pubkey::from_str(&account.keyed_account.pubkey).unwrap(); + let mut account_info: AccountSharedData = + account.keyed_account.account.decode().unwrap(); + + let mut mint = + spl_token::state::Mint::unpack(&account_info.data()[..Mint::LEN]).unwrap(); + let payer = ctx.payer.pubkey(); + mint.mint_authority.replace(payer); + + let mint_bytes = &mut [0; Mint::LEN]; + spl_token::state::Mint::pack(mint, mint_bytes).unwrap(); + + account_info.data_as_mut_slice()[..Mint::LEN].copy_from_slice(mint_bytes); + + ctx.set_account(&address, &account_info); + + (address, account_info) + }; + let mint = spl_token::state::Mint::unpack(&account_info.data()[..Mint::LEN]).unwrap(); + + MintFixture { + test_ctx: ctx_ref, + key: address, + mint, + token_program: account_info.owner().to_owned(), + } + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs new file mode 100644 index 000000000..ce6213925 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -0,0 +1,8 @@ +/// Common functions for the matching engine program tests. +/// + +pub mod token_account; +pub mod mint; +pub mod upgrade_manager; +pub mod airdrop; +// pub mod constants; diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/programs/matching-engine/tests/utils/token_account.rs new file mode 100644 index 000000000..471d36055 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/token_account.rs @@ -0,0 +1,166 @@ +use solana_sdk::{program_pack::Pack, transaction::Transaction, pubkey::Pubkey, signature::Keypair, signer::Signer}; +use anchor_spl::token::spl_token; +use anchor_spl::associated_token::spl_associated_token_account; +use solana_program_test::{ProgramTest, ProgramTestContext}; +use serde_json::Value; +use std::{cell::RefCell, fs, rc::Rc, str::FromStr}; + + +pub struct TokenAccountFixture { + pub test_ctx: Rc>, + pub address: Pubkey, + pub account: spl_token::state::Account, +} + +impl std::fmt::Debug for TokenAccountFixture { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TokenAccountFixture {{ address: {}, account: {:?} }}", self.address, self.account) + } +} + +/// Creates a token account for the given owner and mint +/// +/// # Arguments +/// +/// * `program_test` - The program test instance +/// * `payer` - The payer of the account +/// * `owner` - The owner of the account +/// * `mint` - The mint of the account +pub async fn create_token_account( + test_ctx: Rc>, + owner: &Keypair, + mint: &Pubkey, +) -> TokenAccountFixture { + + let test_ctx_ref = Rc::clone(&test_ctx); + + // Derive the Associated Token Account (ATA) for fee_recipient + let token_account_address = spl_associated_token_account::get_associated_token_address( + &owner.pubkey(), + mint, + ); + + // Inspired by https://github.com/mrgnlabs/marginfi-v2/blob/3b7bf0aceb684a762c8552412001c8d355033119/test-utils/src/spl.rs#L56 + let token_account = { + let mut ctx = test_ctx.borrow_mut(); + + // Create instruction using borrowed values + let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( + &ctx.payer.pubkey(), // Funding account + &owner.pubkey(), // Wallet address + mint, // Mint address + &spl_token::id(), // Token program + ); + + // Create and process transaction + let tx = Transaction::new_signed_with_payer( + &[create_ata_ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await.unwrap(); + + // Get the account + ctx.banks_client + .get_account(token_account_address) + .await + .unwrap() + .unwrap_or_else(|| panic!("Failed to get token account")) + }; + TokenAccountFixture { + test_ctx: test_ctx_ref, + address: token_account_address, + account: spl_token::state::Account::unpack(&token_account.data).unwrap(), + } +} + +/// Reads a keypair from a JSON fixture file +/// +/// Reads the JSON file and parses it into a Value object that is used to extract the keypair. +/// +/// # Arguments +/// +/// * `filename` - The path to the JSON fixture file +pub fn read_keypair_from_file(filename: &str) -> Keypair { + // Read the JSON file + let data = fs::read_to_string(filename) + .expect("Unable to read file"); + + // Parse JSON array into Vec + let bytes: Vec = serde_json::from_str(&data) + .expect("File content must be a JSON array of integers"); + + // Create keypair from bytes + Keypair::from_bytes(&bytes) + .expect("Bytes must form a valid keypair") +} + +// FIXME: This does not work, using the function in the mint.rs file instead +/// Adds an account from a JSON fixture file to the program test +/// +/// Loads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. +/// +/// # Arguments +/// +/// * `program_test` - The program test instance +/// * `filename` - The path to the JSON fixture file +#[allow(dead_code, unused_variables)] +pub fn add_account_from_file( + program_test: &mut ProgramTest, + filename: &str, +) { + // Parse the JSON file to an AccountFixture struct + let account_fixture = read_account_from_file(filename); + // Add the account to the program test + program_test.add_account_with_file_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, filename) +} + +#[allow(dead_code, unused_variables)] +struct AccountFixture { + pub address: Pubkey, + pub owner: Pubkey, + pub lamports: u64, +} + +// FIXME: This code is not being used, remove it + +/// Reads an account from a JSON fixture file +/// +/// Reads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. +/// +/// # Arguments +/// +/// * `filename` - The path to the JSON fixture file +/// +/// # Returns +/// +/// An AccountFixture struct containing the address, owner, lamports, and filename. +fn read_account_from_file( + filename: &str, +) -> AccountFixture { + // Read the JSON file + let data = fs::read_to_string(filename) + .expect("Unable to read file"); + + // Parse the JSON + let json: Value = serde_json::from_str(&data) + .expect("Unable to parse JSON"); + + // Extract the lamports value + let lamports = json["account"]["lamports"] + .as_u64() + .expect("lamports field not found or invalid"); + + // Extract the address value + let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["pubkey"].as_str().expect("pubkey field not found or invalid")).expect("Pubkey field in file is not a valid pubkey"); + // Extract the owner address value + let owner: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["account"]["owner"].as_str().expect("owner field not found or invalid")).expect("Owner field in file is not a valid pubkey"); + + AccountFixture { + address, + owner, + lamports, + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/upgrade_manager.rs b/solana/programs/matching-engine/tests/utils/upgrade_manager.rs new file mode 100644 index 000000000..0d4f31bd7 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/upgrade_manager.rs @@ -0,0 +1,40 @@ +use solana_program_test::ProgramTest; +use solana_sdk::pubkey::Pubkey; +use solana_program::bpf_loader_upgradeable; + +// TODO: Use this function in the test +fn get_program_data(owner: Pubkey) -> Vec { + let state = solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(owner), + }; + bincode::serialize(&state).unwrap() +} + +/// Initialise the upgrade manager program +/// +/// Returns the program data pubkey +pub fn initialise_upgrade_manager(program_test: &mut ProgramTest, program_id: &Pubkey, owner_pubkey: Pubkey) -> Pubkey { + let program_data_pubkey = Pubkey::find_program_address( + &[program_id.as_ref()], + &bpf_loader_upgradeable::id(), + ).0; + + // Add the program data to the program test + // Compute lamports from length of program data + let program_data_data = get_program_data(owner_pubkey.clone()); + + let lamports = solana_sdk::rent::Rent::default().minimum_balance(program_data_data.len()); + let account = solana_sdk::account::Account { + lamports, + data: program_data_data, + owner: bpf_loader_upgradeable::id(), + executable: false, + rent_epoch: u64::MAX, + }; + + program_test.add_account(program_data_pubkey, account); + program_test.add_program("upgrade_manager", common::UPGRADE_MANAGER_PROGRAM_ID, None); + + program_data_pubkey +} \ No newline at end of file From 43720d9a641f9a74f31a121865fca9a714ebf6a2 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 7 Feb 2025 19:27:58 +0000 Subject: [PATCH 005/112] creating vaas for deposits and fts work --- solana/programs/matching-engine/Cargo.toml | 3 + solana/programs/matching-engine/src/lib.rs | 6 +- .../fixtures/accounts/core_bridge/config.json | 11 + .../accounts/core_bridge/fee_collector.json | 11 + .../accounts/core_bridge/guardian_set_0.json | 11 + .../message_transmitter_config.json | 14 + .../testnet/matching_engine_custodian.json | 14 + .../testnet/token_router_custodian.json | 14 + .../token_router_program_data_hacked.json | 14 + .../arbitrum_remote_token_messenger.json | 14 + .../ethereum_remote_token_messenger.json | 14 + .../misconfigured_remote_token_messenger.json | 14 + .../token_messenger.json | 14 + .../token_messenger_minter/token_minter.json | 14 + .../usdc_custody_token.json | 14 + .../usdc_local_token.json | 14 + .../usdc_token_pair.json | 14 + .../tests/fixtures/accounts/usdc_mint.json | 14 + .../fixtures/accounts/usdc_payer_token.json | 14 + .../matching-engine/tests/fixtures/lup.json | 14 + .../mainnet_cctp_message_transmitter.so | Bin 0 -> 1228720 bytes .../mainnet_cctp_token_messenger_minter.so | Bin 0 -> 1617968 bytes .../tests/fixtures/mainnet_core_bridge.so | Bin 0 -> 973360 bytes .../tests/fixtures/token_router.so | Bin 0 -> 704880 bytes .../tests/initialize_integration_tests.rs | 267 ++++++------- .../tests/utils/account_fixtures.rs | 139 +++++++ .../matching-engine/tests/utils/airdrop.rs | 1 - .../matching-engine/tests/utils/auction.rs | 13 + .../matching-engine/tests/utils/constants.rs | 40 +- .../matching-engine/tests/utils/initialize.rs | 156 ++++++++ .../tests/utils/lookup_table.rs | 113 ++++++ .../matching-engine/tests/utils/mod.rs | 12 +- ...upgrade_manager.rs => program_fixtures.rs} | 23 +- .../matching-engine/tests/utils/router.rs | 232 ++++++++++++ .../tests/utils/token_account.rs | 104 ++---- .../tests/utils/transfer_ownership.rs | 32 ++ .../matching-engine/tests/utils/vaa.rs | 352 ++++++++++++++++++ 37 files changed, 1526 insertions(+), 210 deletions(-) create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json create mode 100644 solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json create mode 100644 solana/programs/matching-engine/tests/fixtures/lup.json create mode 100644 solana/programs/matching-engine/tests/fixtures/mainnet_cctp_message_transmitter.so create mode 100644 solana/programs/matching-engine/tests/fixtures/mainnet_cctp_token_messenger_minter.so create mode 100644 solana/programs/matching-engine/tests/fixtures/mainnet_core_bridge.so create mode 100755 solana/programs/matching-engine/tests/fixtures/token_router.so create mode 100644 solana/programs/matching-engine/tests/utils/account_fixtures.rs create mode 100644 solana/programs/matching-engine/tests/utils/auction.rs create mode 100644 solana/programs/matching-engine/tests/utils/initialize.rs create mode 100644 solana/programs/matching-engine/tests/utils/lookup_table.rs rename solana/programs/matching-engine/tests/utils/{upgrade_manager.rs => program_fixtures.rs} (59%) create mode 100644 solana/programs/matching-engine/tests/utils/router.rs create mode 100644 solana/programs/matching-engine/tests/utils/transfer_ownership.rs create mode 100644 solana/programs/matching-engine/tests/utils/vaa.rs diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index bb7c3057d..fda8042bc 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -40,6 +40,7 @@ hex.workspace = true ruint.workspace = true cfg-if.workspace = true + [dev-dependencies] hex-literal.workspace = true solana-program-test = "1.18.15" @@ -50,6 +51,8 @@ solana-cli-output = "1.18.15" base64 = "0.22.1" lazy_static = "1.4.0" bs58 = "0.5.0" +serde = { version = "1.0.212", features = ["derive"] } +secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context"] } [lints] workspace = true diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 07f92f66f..61b4d3f94 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -37,9 +37,9 @@ cfg_if::cfg_if! { } } -const AUCTION_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"auction-custody"; -const LOCAL_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"local-custody"; -const PREPARED_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"prepared-custody"; +pub const AUCTION_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"auction-custody"; +pub const LOCAL_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"local-custody"; +pub const PREPARED_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"prepared-custody"; const FEE_PRECISION_MAX: u32 = 1_000_000; const VAA_AUCTION_EXPIRATION_TIME: i64 = 2 * 60 * 60; // 2 hours diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json new file mode 100644 index 000000000..1806e2e29 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json @@ -0,0 +1,11 @@ +{ + "pubkey": "2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn", + "account": { + "lamports": 1057920, + "data": ["AAAAAMbrG4wAAAAAgFEBAAoAAAAAAAAA", "base64"], + "owner": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 24 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json new file mode 100644 index 000000000..2e08f6482 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json @@ -0,0 +1,11 @@ +{ + "pubkey": "9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy", + "account": { + "lamports": 2350640070, + "data": ["", "base64"], + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 0 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json new file mode 100644 index 000000000..3cd95bced --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json @@ -0,0 +1,11 @@ +{ + "pubkey": "DS7qfSAgYsonPpKoAjcGhX9VFjXdGkiHjEDkTidf8H2P", + "account": { + "lamports": 21141440, + "data": ["AAAAAAEAAAC++kKdV80Yt/ik2RotqatK8F0PvkPJm2EAAAAA", "base64"], + "owner": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 36 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json b/solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json new file mode 100644 index 000000000..0e3e24891 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json @@ -0,0 +1,14 @@ +{ + "pubkey": "BWrwSWjbikT3H7qHAkUEbLmwDQoB4ZDJ4wcSEhSPTZCu", + "account": { + "lamports": 2519520, + "data": [ + "Ryi0jhPLI/wfOQgPIIpMNon4r0rVMO7Sy1fUtUxQmdUxE51OObvhOoDFz5C0iApooK3hQfndo8m3eRHbcLcd6T35aIm/9s3sgMXPkLSICmigreFB+d2jybd5Edtwtx3pPfloib/2zeyAxc+QtIgKaKCt4UH53aPJt3kR23C3Hek9+WiJv/bN7AAFAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAvvpCnVfNGLf4pNkaLamrSvBdD77QBwAAAAAAACYAAAAAAAAA/w==", + "base64" + ], + "owner": "CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 234 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json new file mode 100644 index 000000000..4d3584ed2 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json @@ -0,0 +1,14 @@ +{ + "pubkey": "5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr", + "account": { + "lamports": 1927920, + "data": [ + "hOSLuHDkbPAMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyYhuGDPpdfzJOBnceTvHX8S6d3fMAJtKCp45SBiW1cXT3n2+4rU8d7ccb0Cv2HoY7eHIPeYyfdHVa3M3rheUeAgAAAAIAAAAAAAAAK/Yehjt4cg95jJ90dVrczeuF5R4BAAAAAQAAAAAAAAA=", + "base64" + ], + "owner": "mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 149 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json new file mode 100644 index 000000000..0402b34b6 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json @@ -0,0 +1,14 @@ +{ + "pubkey": "CFYdtHYDnQgCAcwetWVjVg5V8Uiy1CpJaoYJxmV19Z7N", + "account": { + "lamports": 1851360, + "data": [ + "hOSLuHDkbPAADBpYhv4Qk9+fxDjClvn3J1t3GLa8DhVtjTNsWPCDmW0AsmIbhgz6XX8yTgZ3Hk7x1/Eund3zACbSgqeOUgYltXGyYhuGDPpdfzJOBnceTvHX8S6d3fMAJtKCp45SBiW1cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 138 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json new file mode 100644 index 000000000..178063391 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json @@ -0,0 +1,14 @@ +{ + "pubkey": "CqrEUyMva5apDRpzbMh52w3JV3NBFVNBiQsr52uewM6j", + "account": { + "lamports": 10102224240, + "data": [ + "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AAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAD3mgAAeWFoAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADrmgAAeWFwAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADfmgAAeWHwAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADTmgAAeWH4AgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADHmgAAeWE4AAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAC7mgAAeWFAAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACvmgAAeWGoAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACjmgAAeWGwAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACXmgAAeWHYAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAA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ALcDAAAIAAAAhRAAAKyZAAB5YcABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAKCZAAB5YegBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAJSZAAB5YfABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAIiZAAB5YRgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAHyZAAB5YSACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAHCZAAB5YUgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAGSZAAB5YVACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAFiZAAB5YXgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAEyZAAB5YYACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAECZAAB5YagCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAADSZAAB5YbACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAACiZAAB5YdgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAByZAAB5YeACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAABCZAAB5YQgDAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAASZAAB5YRADAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAPiYAAB5YTgDAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAOyYAAB5YUADAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAOCYAAB5YWgDAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAANSYAAB5YXADAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAMiYAAB5YZgDAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAALyYAAB5YaADAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAALCYAAB5YcgDAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAKSYAAB5YdADAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAJiYAACVAAAAAAAAAL8WAAAAAAAAeWHIAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACKmAAAeWHQAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAB+mAAAeWH4AAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABymAAAeWEAAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABmmAAAeWEoAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABamAAAeWEwAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABOmAAAeWGYAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABCmAAAeWGgAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAA2mAAAeWFYAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAqmAAAeWFgAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAemAAAeWGIAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAASmAAAeWGQAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAGmAAAeWG4AQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAD6lwAAeWHAAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADulwAAeWHoAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADilwAAeWHwAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADWlwAAeWEYAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADKlwAAeWEgAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAC+lwAAeWFIAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACylwAAeWFQAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACmlwAAeWF4AgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACalwAAeWGAAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACOlwAAeWGoAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACClwAAeWGwAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIA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AAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAALOVAAB5YTABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAKeVAAB5YZgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAJuVAAB5YaAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAI+VAAB5YVgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAIOVAAB5YWABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAHeVAAB5YYgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAGuVAAB5YZABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAF+VAAB5YbgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAFOVAAB5YcABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAEeVAACVAAAAAAAAAL8WAAAAAAAAeWEIAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAA5lQAAeWEQAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAtlQAAeWFIBAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAhlQAAeWFQBAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAVlQAAeWGABAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAJlQAAeWGIBAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAD9lAAAv2EAAAAAAAAHAQAAMAAAAIUQAAAT+P//eWGIAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADulAAAeWGQAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADilAAAeWG4AAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADWlAAAeWHAAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADKlAAAeWHoAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAC+lAAAeWHwAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACylAAAeWE4BQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACmlAAAeWFABQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACalAAAeWEYAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACOlAAAeWEgAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACClAAAeWFIAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAB2lAAAeWFQAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABqlAAAeWF4AQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABelAAAeWGAAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABSlAAAeWGoAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABGlAAAeWGwAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAA6lAAAeWHYAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAulAAAeWHgAQAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAilAAAeWEIAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAWlAAAeWEQAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAKlAAAeWE4AgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAD+kwAAeWFAAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADykwAAeWFoAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADmkwAAeWFwAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADakwAAeWGYAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADOkwAAeWGgAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADCkwAAeWHIAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAC2kwAAeWHQAgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACqkwAAeWH4AgAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACekwAAeWEAAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACSkwAAeWEoAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACGkwAAeWEwAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAB6kwAAeWFYAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABukwAAeWFgAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABikwAAeWGIAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABWkwAAeWGQAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABKkwAAeWG4AwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAA+kwAAeWHAAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAykwAAeWHoAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAmkwAAeWHwAwAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAakwAAeWEYBAAAAAB5EgAAAAAAAAcCAAD/////eyEA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AP////97IQAAAAAAAL+hAAAAAAAABwEAALj1//+FEAAA5Jz//7+hAAAAAAAABwEAAJD0//+FEAAA4Zz//7+hAAAAAAAABwEAAGD0//+FEAAA3pz//7+hAAAAAAAABwEAADD0//+FEAAA25z//7+hAAAAAAAABwEAAAD0//+FEAAA2Jz//7+hAAAAAAAABwEAANDz//+FEAAA1Zz//7+hAAAAAAAABwEAAKDz//+FEAAA0pz//7+hAAAAAAAABwEAAHDz//+FEAAAz5z//7+hAAAAAAAABwEAAEDz//+FEAAAzJz//7+hAAAAAAAABwEAABDz//+FEAAAyZz//7+hAAAAAAAABwEAAODy//+FEAAAxpz//7+hAAAAAAAABwEAALDy//+FEAAAw5z//7+hAAAAAAAABwEAAIDy//+FEAAAwJz//7+hAAAAAAAABwEAAFDy//+FEAAAvZz//7+hAAAAAAAABwEAAEjy//+FEAAAO57//3mhIPIAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAFzoAAHmhKPIAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAACzoAAHmh8PEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA/zkAAHmh+PEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAA8zkAAHmhwPEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA5zkAAHmhyPEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAA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ALcCAAAoAAAAtwMAAAgAAACFEAAAURIAAHmh8PcAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAARRIAAHmh+PcAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAORIAAHmhIPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAALRIAAHmhKPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAIRIAAHmhUPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAFRIAAHmhWPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAACRIAAHmhgPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA/REAAHmhiPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAA8REAAHmhsPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA5REAAHmhuPgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAA2REAAHmh4PgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAzREAAHmh6PgAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAwREAAHmhEPkAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAtREAAL+mAAAAAAAABwYAAGj5//95oRj5AAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAKcRAAC/YQAAAAAAAIUQAAAwev//eaFA+QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACZEQAAeaFI+QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACNEQAAeaGg8wAAAABVAQEAAAAAAAUAOgEAAAAAeaHo9AAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAB+EQAAeaHw9AAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAByEQAAeaEY9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABmEQAAeaEg9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABaEQAAeaFI9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAABOEQAAeaFQ9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAABCEQAAeaF49QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAA2EQAAeaGA9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAAqEQAAeaGo9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAeEQAAeaGw9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAASEQAAeaHY9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAAGEQAAeaHg9QAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAAD6EAAAeaEI9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADuEAAAeaEQ9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADiEAAAeaE49gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAADWEAAAeaFA9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAADKEAAAeaFo9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAC+EAAAeaFw9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACyEAAAeaGY9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAACmEAAAeaGg9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACaEAAAeaHI9gAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAA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AAAAAAB5YQgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAGL///95YRAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAFb///95YTgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAEr///95YUAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAD7///95YWgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAADL///95YXAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAACb///95YZgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAABr///95YaAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAA7///95YcgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAAL///95YdAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAPb+//95YfgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAOr+//95YQABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAN7+//95YSgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAANL+//95YTABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAMb+//95YVgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAALr+//95YWABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAK7+//95YYgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAKL+//95YZABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAJb+//95YbgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQID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AAAAAAC/YwAAAAAAAIUQAADKbAAAlQAAAAAAAAC/FgAAAAAAAHlhGAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAATvH//3lhIAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAQvH//3lhSAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAANvH//3lhUAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAKvH//3lheAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAHvH//3lhgAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAEvH//3lhqAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAABvH//3lhsAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAA+vD//3lh2AAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA7vD//3lh4AAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAA4vD//3lhCAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAA1vD//3lhEAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAyvD//3lhOAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAvvD//3lhQAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAsvD//3lhaAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAApvD//3lhcAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAmvD//3lhmAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAjvD//3lhoAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAgvD//79hAAAAAAAABwEAAPABAACFEAAAuQEAAHlhyAEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAc/D//3lh0AEAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAZ/D//5UAAAAAAAAAvxYAAAAAAAB5YRgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAFnw//95YSAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAE3w//95YUgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAEHw//95YVAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAADXw//95YXgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAACnw//95YYAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAB3w//95YagAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAABHw//95YbAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAAXw//95YdgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAPnv//95YeAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAO3v//95YQgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAOHv//95YRABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAANXv//95YTgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAMnv//95YUABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAL3v//95YWgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAALHv//95YXABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAKXv//95YZgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAJnv//95YaABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAI3v//95YcgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAIHv//95YdABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAHXv//95YfgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAGnv//95YQACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAF3v//95YSgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAFHv//95YTACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAEXv//95YVgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAADnv//95YWACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAC3v//95YYgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAACHv//95YZACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAABXv//95YbgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAAnv//95YcACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAP3u//+/YQAAAAAAAAcBAAAQAwAAhRAAADQAAAB5YegCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAO7u//95YfACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAOLu//+VAAAAAAAAAJUAAAAAAAAAeRIAAAAAAAAVAgMAAAAAAHkRCAAAAAAAtwMAAAEAAACFEAAA2+7//5UAAAAAAAAAeRYAAAAAAAC/YQAAAAAAAFcBAAADAAAAVQEPAAEAAAB5YQcAAAAAAHkSAAAAAAAAeWH//wAAAACNAAAAAgAAAHljBwAAAAAABwYAAP////95MggAAAAAABUCAwAAAAAAeWEAAAAAAAB5MxAAAAAAAIUQAADL7v//v2EAAAAAAAC3AgAAGAAAALcDAAAIAAAAhRAAAMfu//+VAAAAAAAAAL8WAAAAAAAAeWcQAAAAAAAVBxMAAAAAAHloCAAAAAAAJwcAADAAAAAHCAAAEAAAAAUAFgAAAAAAeYEAAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACz7v//BwgAADAAAAAHBwAA0P///1UHBwAAAAAAeWIAAAAAAAAVAhIAAAAAAHlhCAAAAAAAJwIAADAAAAC3AwAACAAAAIUQAACq7v//BQANAAAAAAB5gfj/AAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUC5f8AAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQLh/wAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAJ3u//8FAN3/AAAAAJUAAAAAAAAAeREAAAAAAACFEAAAJQAAALcAAAAAAAAAlQAAAAAAAAB5EQAAAAAAAHsayP8AAAAAv6YAAAAAAAAHBgAA0P///79hAAAAAAAAtwMAADAAAACFEAAAhX0AAL+hAAAAAAAABwEAAMj///8YAgAAeCYKAAAAAAAAAAAAv2MAAAAAAACFEAAA+GkAAJUAAAAAAAAAvzYAAAAAAAC/KAAAAAAAAHkXAAAAAAAAeXkQAAAAAAB5cQAAAAAAAB+RAAAAAAAAPWEFAAAAAAC/cQAAAAAAAL+SAAAAAAAAv2MAAAAAAACFEAAAawEAAHl5EAAAAAAAeXEIAAAAAAAPkQAAAAAAAL+CAAAAAAAAv2MAAAAAAACFEAAAbX0AAA9pAAAAAAAAe5cQAAAAAAC3AAAAAAAAAJUAAAAAAAAAvycAAAAAAAC/FgAAAAAAAL9xAAAAAAAAZwEAACAAAAB3AQAAIAAAALcCAACAAAAALRIOAAAAAAC3AgAAAAAAAGMq/P8AAAAAtwIAAAAIAAAtEgEAAAAAAAUAFQAAAAAAv3EAAAAAAABXAQAAPwAAAEcBAACAAAAAcxr9/wAAAAB3BwAABgAAAEcHAADAAAAAc3r8/wAAAAC3BwAAAgAAAAUAMAAAAAAAeWIQAAAAAAB5YQAAAAAAAF0SAwAAAAAAv2EAAAAAAACFEAAAewAAAHliEAAAAAAAeWEIAAAAAAAPIQAAAAAAAHNxAAAAAAAABwIAAAEAAAB7JhAAAAAAAAUANQAAAAAAv3EAAAAAAABnAQAAIAAAAHcBAAAgAAAAtwIAAAAAAQAtEhMAAAAAAFcHAAA/AAAARwcAAIAAAABzev//AAAAAL8SAAAAAAAAdwIAAAYAAABXAgAAPwAAAEcCAACAAAAAcyr+/wAAAAC/EgAAAAAAAHcCAAAMAAAAVwIAAD8AAABHAgAAgAAAAHMq/f8AAAAAdwEAABIAAABXAQAABwAAAEcBAADwAAAAcxr8/wAAAAC3BwAABAAAAAUADAAAAAAAVwcAAD8AAABHBwAAgAAAAHN6/v8AAAAAvxIAAAAAAAB3AgAADAAAAEcCAADgAAAAcyr8/wAAAAB3AQAABgAAAFcBAAA/AAAARwEAAIAAAABzGv3/AAAAALcHAAADAAAAeWgQAAAAAAB5YQAAAAAAAB+BAAAAAAAAPXEFAAAAAAC/YQAAAAAAAL+CAAAAAAAAv3MAAAAAAACFEAAAFAEAAHloEAAAAAAAeWEIAAAAAAAPgQAAAAAAAL+iAAAAAAAABwIAAPz///+/cwAAAAAAAIUQAAAVfQAAD3gAAAAAAAB7hhAAAAAAALcAAAAAAAAAlQAAAAAAAAC/NgAAAAAAAL8oAAAAAAAAvxcAAAAAAAB5eRAAAAAAAHlxAAAAAAAAH5EAAAAAAAA9YQUAAAAAAL9xAAAAAAAAv5IAAAAAAAC/YwAAAAAAAIUQAAD+AAAAeXkQAAAAAAB5cQgAAAAAAA+RAAAAAAAAv4IAAAAAAAC/YwAAAAAAAIUQAAAAfQAAD2kAAAAAAAB7lxAAAAAAALcAAAAAAAAAlQAAAAAAAAC/OAAAAAAAAL8nAAAAAAAAvxYAAAAAAAAVCAoAAAAAAHlBEAAAAAAAFQERAAAAAAB5QggAAAAAAFUCCQAAAAAAFQcYAAAAAAC/cQAAAAAAAL+CAAAAAAAAhRAAAPnt//8VABAAAAAAAAUAFQAAAAAAtwEAAAAAAAB7FhAAAAAAAAUADQAAAAAAeUEAAAAAAAC/gwAAAAAAAL90AAAAAAAAhRAAAPTt//8VAAcAAAAAAAUADAAAAAAAFQcJAAAAAAC/cQAAAAAAAL+CAAAAAAAAhRAAAOrt//8VAAEAAAAAAAUABgAAAAAAe4YQAAAAAAB7dggAAAAAALcBAAABAAAABQAFAAAAAAC3BwAAAAAAAL+AAAAAAAAAe3YQAAAAAAB7BggAAAAAALcBAAAAAAAAexYAAAAAAACVAAAAAAAAAL8WAAAAAAAABwIAAAEAAAC3AQAAAQAAABUCAQAAAAAAtwEAAAAAAABXAQAAAQAAAFUBJAAAAAAAeWEAAAAAAAC/FwAAAAAAAGcHAAABAAAALScBAAAAAAC/JwAAAAAAACUHAQAIAAAAtwcAAAgAAAC/cwAAAAAAAKcDAAD/////dwMAAD8AAAAVAQYAAAAAAHliCAAAAAAAtwQAAAEAAAB7Svj/AAAAAHsa8P8AAAAAeyro/wAAAAAFAAIAAAAAALcBAAAAAAAAexr4/wAAAAC/oQAAAAAAAAcBAADQ////v6QAAAAAAAAHBAAA6P///79yAAAAAAAAhRAAALj///95odj/AAAAAHmi0P8AAAAAVQIDAAAAAAB7dgAAAAAAAHsWCAAAAAAAlQAAAAAAAAB5ouD/AAAAABgDAAABAAAAAAAAAAAAAIAdMvv/AAAAAFUCAgAAAAAAhRAAAO5TAACFEAAA/////4UQAAD9UwAAhRAAAP////+/FgAAAAAAAAcCAAABAAAAtwEAAAEAAAAVAgEAAAAAALcBAAAAAAAAVwEAAAEAAABVASkAAAAAAHlhAAAAAAAAvxcAAAAAAABnBwAAAQAAAC0nAQAAAAAAvycAAAAAAAAlBwEABAAAALcHAAAEAAAAtwMAAAEAAAAYAgAAq6qqqgAAAACqqqoCLXIBAAAAAAC3AwAAAAAAAL9yAAAAAAAAJwIAADAAAABnAwAAAwAAABUBBwAAAAAAeWQIAAAAAAC3BQAACAAAAHta+P8AAAAAJwEAADAAAAB7GvD/AAAAAHtK6P8AAAAABQACAAAAAAC3AQAAAAAAAHsa+P8AAAAAv6EAAAAAAAAHAQAA0P///7+kAAAAAAAABwQAAOj///+FEAAAhP///3mh2P8AAAAAeaLQ/wAAAABVAgMAAAAAAHt2AAAAAAAAexYIAAAAAACVAAAAAAAAAHmi4P8AAAAAGAMAAAEAAAAAAAAAAAAAgB0y+/8AAAAAVQICAAAAAACFEAAAulMAAIUQAAD/////hRAAAMlTAACFEAAA/////78WAAAAAAAAvyQAAAAAAAAPNAAAAAAAALcBAAABAAAALUIBAAAAAAC3AQAAAAAAAFcBAAABAAAAVQEoAAAAAAB5YQAAAAAAAL8XAAAAAAAAZwcAAAEAAAAtRwEAAAAAAL9HAAAAAAAAJQcBAAQAAAC3BwAABAAAALcDAAABAAAAGAIAAMTDw8MAAAAAw8PDAy1yAQAAAAAAtwMAAAAAAAC/cgAAAAAAACcCAAAiAAAAFQEHAAAAAAB5ZAgAAAAAALcFAAABAAAAe1r4/wAAAAAnAQAAIgAAAHsa8P8AAAAAe0ro/wAAAAAFAAIAAAAAALcBAAAAAAAAexr4/wAAAAC/oQAAAAAAAAcBAADQ////v6QAAAAAAAAHBAAA6P///4UQAABQ////eaHY/wAAAAB5otD/AAAAAFUCAwAAAAAAe3YAAAAAAAB7FggAAAAAAJUAAAAAAAAAeaLg/wAAAAAYAwAAAQAAAAAAAAAAAACAHTL7/wAAAABVAgIAAAAAAIUQAACGUwAAhRAAAP////+FEAAAlVMAAIUQAAD/////vxYAAAAAAAC/JAAAAAAAAA80AAAAAAAAtwEAAAEAAAAtQgEAAAAAALcBAAAAAAAAVwEAAAEAAABVASkAAAAAAHlhAAAAAAAAvxcAAAAAAABnBwAAAQAAAC1HAQAAAAAAv0cAAAAAAAAlBwEABAAAALcHAAAEAAAAtwMAAAEAAAAYAgAAq6qqqgAAAACqqqoCLXIBAAAAAAC3AwAAAAAAAL9yAAAAAAAAJwIAADAAAABnAwAAAwAAABUBBwAAAAAAeWQIAAAAAAC3BQAACAAAAHta+P8AAAAAJwEAADAAAAB7GvD/AAAAAHtK6P8AAAAABQACAAAAAAC3AQAAAAAAAHsa+P8AAAAAv6EAAAAAAAAHAQAA0P///7+kAAAAAAAABwQAAOj///+FEAAAG////3mh2P8AAAAAeaLQ/wAAAABVAgMAAAAAAHt2AAAAAAAAexYIAAAAAACVAAAAAAAAAHmi4P8AAAAAGAMAAAEAAAAAAAAAAAAAgB0y+/8AAAAAVQICAAAAAACFEAAAUVMAAIUQAAD/////hRAAAGBTAACFEAAA/////78WAAAAAAAAvyQAAAAAAAAPNAAAAAAAALcBAAABAAAALUIBAAAAAAC3AQAAAAAAAFcBAAABAAAAVQEkAAAAAAB5YQAAAAAAAL8XAAAAAAAAZwcAAAEAAAAtRwEAAAAAAL9HAAAAAAAAJQcBAAgAAAC3BwAACAAAAL9zAAAAAAAApwMAAP////93AwAAPwAAABUBBgAAAAAAeWIIAAAAAAC3BAAAAQAAAHtK+P8AAAAAexrw/wAAAAB7Kuj/AAAAAAUAAgAAAAAAtwEAAAAAAAB7Gvj/AAAAAL+hAAAAAAAABwEAAND///+/pAAAAAAAAAcEAADo////v3IAAAAAAACFEAAA6/7//3mh2P8AAAAAeaLQ/wAAAABVAgMAAAAAAHt2AAAAAAAAexYIAAAAAACVAAAAAAAAAHmi4P8AAAAAGAMAAAEAAAAAAAAAAAAAgB0y+/8AAAAAVQICAAAAAACFEAAAIVMAAIUQAAD/////hRAAADBTAACFEAAA/////78mAAAAAAAAexoQ/wAAAAB5MSgAAAAAAHsaSP8AAAAAeTEgAAAAAAB7Gij/AAAAAHk4GAAAAAAAeTEQAAAAAAB7GlD/AAAAAHkxCAAAAAAAexow/wAAAAB5MQAAAAAAAHsaGP8AAAAABwIAABAAAAC/oQAAAAAAAAcBAABY////eyo4/wAAAACFEAAARwwAAHlpIAMAAAAAv5EAAAAAAAAnAQAAIgAAAHtqQP8AAAAAe4og/wAAAAB7Ggj/AAAAAFUJlwAAAAAAtwIAAAEAAAC3CAAAAAAAAHmmaP8AAAAAv2MAAAAAAAAnAwAAIgAAAHmhYP8AAAAADzEAAAAAAAC/gwAAAAAAACcDAAAiAAAAvycAAAAAAACFEAAAtHsAAA+GAAAAAAAAe2po/wAAAAAVCQQAAAAAAL9xAAAAAAAAeaII/wAAAAC3AwAAAQAAAIUQAAC47P//twcAAAAEAAC3AQAAAAQAALcCAAABAAAAhRAAALLs//+/CQAAAAAAAHmmUP8AAAAAVQkEAAAAAAC3AQAAAAQAALcCAAABAAAAhRAAAPpSAACFEAAA/////3uagP8AAAAAe3p4/wAAAAAYAQAAJpB/4QAAAAAf4e4ZexkAAAAAAAAYAQAA/////wAAAAAAAAAALRbKAAAAAABjaQgAAAAAALcIAAAMAAAAe4qI/wAAAAC3AQAA9QMAAC1hBwAAAAAAv6EAAAAAAAAHAQAAeP///7cCAAAMAAAAv2MAAAAAAACFEAAAiP///3mpgP8AAAAAeaiI/wAAAAC/kQAAAAAAAA+BAAAAAAAAeaIw/wAAAAC/YwAAAAAAAIUQAACJewAAD2gAAAAAAAB7ioj/AAAAABgBAAD/////AAAAAAAAAAB5p0j/AAAAAC0XsQAAAAAAeaF4/wAAAAC/EgAAAAAAAB+CAAAAAAAAJQIIAAMAAAC/oQAAAAAAAAcBAAB4////v4IAAAAAAAC3AwAABAAAAIUQAABy////eamA/wAAAAB5oXj/AAAAAHmoiP8AAAAAv5IAAAAAAAAPggAAAAAAAGNyAAAAAAAABwgAAAQAAAB7ioj/AAAAAB+BAAAAAAAALReVAAAAAAB5poD/AAAAAA+JAAAAAAAAv5EAAAAAAAB5oij/AAAAAL9zAAAAAAAAhRAAAGp7AAB5oXj/AAAAAFUGDAAAAAAAexp4/wAAAAC/owAAAAAAAAcDAAB4////GAEAAEDbCQAAAAAAAAAAALcCAAArAAAAGAQAAMAlCgAAAAAAAAAAABgFAADgJQoAAAAAAAAAAACFEAAAMGMAAIUQAAD/////GAIAAI60XdEAAAAA2AUIznsqkP8AAAAAGAIAAM3AvzwAAAAAGJgHLXsqiP8AAAAAGAIAAHWfOlQAAAAAYFjvzXsqgP8AAAAAGAIAAKZfyYkAAAAA219dQnsqeP8AAAAAeaJY/wAAAAB7Kpj/AAAAAHmiYP8AAAAAeyqg/wAAAAB5omj/AAAAAHsqqP8AAAAAe2q4/wAAAAB7GrD/AAAAAA94AAAAAAAAe4rA/wAAAAC/oQAAAAAAAAcBAADI////eaI4/wAAAACFEAAAaQkAAHmk2P8AAAAAeaHI/wAAAAAfQQAAAAAAAHmiQP8AAAAAeSgYAwAAAAB5JyADAAAAAD1xBgAAAAAAv6EAAAAAAAAHAQAAyP///79CAAAAAAAAv3MAAAAAAACFEAAA+f7//3mk2P8AAAAAeaHQ/wAAAAB7Gjj/AAAAABUHgwAAAAAAJwcAADAAAAC/gQAAAAAAAA9xAAAAAAAAexpI/wAAAAC/QgAAAAAAACcCAAAwAAAAeaE4/wAAAAAPEgAAAAAAAAcCAAAqAAAABQCCAAAAAAB7mvj+AAAAACcJAAAwAAAAtwcAAAEAAAAYAgAAkbS0tAAAAAC0tLS0LZIBAAAAAAC3BwAAAAAAAC2SAgAAAAAAhRAAAGVSAACFEAAA/////3lmGAMAAAAAv3IAAAAAAACFEAAAJOz//xUAUQAAAAAAtwcAAP////8HBgAAKAAAAHsKAP8AAAAAvwgAAAAAAAAFACgAAAAAAHlh2P8AAAAAeRIYAAAAAAB7Kvj/AAAAAHkSEAAAAAAAeyrw/wAAAAB5EggAAAAAAHsq6P8AAAAAeREAAAAAAAB7GuD/AAAAAHFjAAAAAAAAv6EAAAAAAAAHAQAAeP///7+iAAAAAAAABwIAAOD///+FEAAA4UUAAL+iAAAAAAAABwIAAHj///+/gQAAAAAAALcDAAAiAAAAhRAAAAF7AAAHBgAAMAAAAAcIAAAiAAAABwcAAAEAAAAHCQAA0P///1UJDwAAAAAAeaZo/wAAAAB5oVj/AAAAAB9hAAAAAAAAv3gAAAAAAAAHCAAAAQAAAHmp+P4AAAAAeaIA/wAAAAAtcTj/AAAAAL+hAAAAAAAABwEAAFj///+/YgAAAAAAAL+DAAAAAAAAhRAAAH7+//95ogD/AAAAAAUAMP8AAAAAcWEBAAAAAABVAdb/AAAAAHlh2P8AAAAAeRIYAAAAAAB7Kvj/AAAAAHkSEAAAAAAAeyrw/wAAAAB5EggAAAAAAHsq6P8AAAAAeREAAAAAAAB7GuD/AAAAAHFjAAAAAAAAv6EAAAAAAAAHAQAAeP///7+iAAAAAAAABwIAAOD///+FEAAAw0UAAAUA1f8AAAAAv6EAAAAAAAAHAQAAeP///7+CAAAAAAAAv3MAAAAAAACFEAAAzv7//3mmgP8AAAAAeaiI/wAAAAC/aQAAAAAAAAUAY/8AAAAAead4/wAAAAAVBwQAAAAAAL+RAAAAAAAAv3IAAAAAAAC3AwAAAQAAAIUQAADX6///GAEAAAMAAAAAAAAAFAAAAAUAYf8AAAAAeaEI/wAAAAC/cgAAAAAAAIUQAAAdUgAAhRAAAP////97dgAAAAAAAFUJAQABAAAABQAmAAAAAAB5hxgAAAAAAHmJIAAAAAAAcYEoAAAAAABxgykAAAAAAHGFKgAAAAAAc1IAAAAAAABzMv//AAAAAHMS/v8AAAAAe5L2/wAAAAB7cu7/AAAAAHti5v8AAAAAe0Le/wAAAAB7Atb/AAAAAAcCAAAwAAAAeaRQ/wAAAAAHBAAAAQAAAAcIAAAwAAAAeaFI/wAAAABdGAkAAAAAAHtK2P8AAAAAeaNA/wAAAAB5NugCAAAAAHlhAAAAAAAABwEAAAEAAAC3AgAAAQAAABUBFQAAAAAAtwIAAAAAAAAFABMAAAAAAHtKUP8AAAAAeYQIAAAAAAB5RgAAAAAAAAcGAAABAAAAtwcAAAEAAAAVBgEAAAAAALcHAAAAAAAAeYAAAAAAAAB7ZAAAAAAAAFUHAgABAAAAhRAAAP////+FEAAA/////3mGEAAAAAAAeWcAAAAAAAAHBwAAAQAAALcJAAABAAAAFQfQ/wAAAAC3CQAAAAAAAAUAzv8AAAAAeTXgAgAAAAB7FgAAAAAAAFUCAQABAAAABQDz/wAAAAB5OPACAAAAAHmBAAAAAAAABwEAAAEAAAC3AgAAAQAAABUBAQAAAAAAtwIAAAAAAAB7GAAAAAAAAFUCAQABAAAABQDq/wAAAAB5oUD/AAAAAHESCgMAAAAAeypI/wAAAABxEgkDAAAAAHsqUP8AAAAAcRkIAwAAAAB5EAADAAAAAHkX+AIAAAAAeaHI/wAAAABdFA8AAAAAAL+hAAAAAAAABwEAAMj///+/QgAAAAAAAHt6CP8AAAAAv1cAAAAAAAB7mgD/AAAAAL8JAAAAAAAAhRAAAM/9//+/kAAAAAAAAHmpAP8AAAAAv3UAAAAAAAB5pwj/AAAAAHmh0P8AAAAAexo4/wAAAAB5pNj/AAAAAL9BAAAAAAAAJwEAADAAAAB5ozj/AAAAAL8yAAAAAAAADxIAAAAAAAB5oUj/AAAAAHMSKgAAAAAAeaFQ/wAAAABzEikAAAAAAHOSKAAAAAAAewIgAAAAAAB7chgAAAAAAHuCEAAAAAAAe2IIAAAAAAB7UgAAAAAAAAcEAAABAAAAe0rY/wAAAAB5pkD/AAAAAHlhCAAAAAAAeWIAAAAAAAB7KgDwAAAAAHsaCPAAAAAAv6EAAAAAAAAHAQAAWP///7+iAAAAAAAABwIAAHj///+/pQAAAAAAAIUQAABwRQAAYaFY/wAAAABVAQQAFgAAALcBAAAEAAAAeaIQ/wAAAAB7EgAAAAAAAAUADAAAAAAAeaFw/wAAAAB7Gvj/AAAAAHmhaP8AAAAAexrw/wAAAAB5oWD/AAAAAHsa6P8AAAAAeaFY/wAAAAB7GuD/AAAAAL+iAAAAAAAABwIAAOD///95oRD/AAAAAIUQAAAZMwAAeacg/wAAAAC/oQAAAAAAAAcBAADI////hRAAAID8//95opj/AAAAABUCBAAAAAAAJwIAACIAAAB5oaD/AAAAALcDAAABAAAAhRAAAEDr//95orD/AAAAABUCAwAAAAAAeaG4/wAAAAC3AwAAAQAAAIUQAAA76///eaIY/wAAAAAVAgMAAAAAAHmhMP8AAAAAtwMAAAEAAACFEAAANuv//xUHBAAAAAAAeaEo/wAAAAC/cgAAAAAAALcDAAABAAAAhRAAADHr//+/YQAAAAAAAIUQAADJ+v//lQAAAAAAAAAYAgAAjrRd0QAAAADYBQjOeyEYAAAAAAAYAgAAzcC/PAAAAAAYmActeyEQAAAAAAAYAgAAdZ86VAAAAABgWO/NeyEIAAAAAAAYAgAApl/JiQAAAADbX11CeyEAAAAAAACVAAAAAAAAAL83AAAAAAAAvyYAAAAAAAB7GvD+AAAAAL+hAAAAAAAABwEAAIj///+/cgAAAAAAALcDAABAAAAAhRAAAA56AAB5cUAAAAAAAHsaCP8AAAAAYXFIAAAAAAB7Gvj+AAAAAL9nAAAAAAAAv6EAAAAAAAAHAQAAGP///79yAAAAAAAAhRAAANIOAAB5c5ACAAAAAL8yAAAAAAAAJwIAACIAAAB7egD/AAAAAHsq6P4AAAAAezoQ/wAAAABVA2YAAAAAALcCAAABAAAAtwkAAAAAAAB5qCj/AAAAAL+DAAAAAAAAJwMAACIAAAB5oSD/AAAAAA8xAAAAAAAAv5MAAAAAAAAnAwAAIgAAAL8mAAAAAAAAhRAAAPN5AAAPmAAAAAAAAHuKKP8AAAAAeaEQ/wAAAAAVAQQAAAAAAL9hAAAAAAAAeaLo/gAAAAC3AwAAAQAAAIUQAAD26v//twYAAAAEAAC3AQAAAAQAALcCAAABAAAAhRAAAPDq//9VAAQAAAAAALcBAAAABAAAtwIAAAEAAACFEAAAOlEAAIUQAAD/////eaH4/gAAAABjEBAAAAAAAHmhCP8AAAAAexAIAAAAAAAYAQAAp94TcgAAAABVFQ52exAAAAAAAAB5oaD/AAAAAHsQLAAAAAAAeaGY/wAAAAB7ECQAAAAAAHmhkP8AAAAAexAcAAAAAAB5oYj/AAAAAHsQFAAAAAAAeaGo/wAAAAB7EDQAAAAAAHmhsP8AAAAAexA8AAAAAAB5obj/AAAAAHsQRAAAAAAAeaHA/wAAAAB7EEwAAAAAAHtqcP8AAAAAewp4/wAAAAC3AQAAVAAAAHsagP8AAAAAGAEAAKZfyUMAAAAAQZpa1XsaOP8AAAAAGAEAAJAEL9YAAAAAfJeR/XsaQP8AAAAAGAEAAAFaz1MAAAAApUzII3saSP8AAAAAGAEAAO24/4EAAAAAue1yLnsaUP8AAAAAeaEY/wAAAAB7Glj/AAAAAHmhIP8AAAAAexpg/wAAAAB5oSj/AAAAAHsaaP8AAAAAv6EAAAAAAAAHAQAAyP///79yAAAAAAAAhRAAAJwMAAB5pNj/AAAAAHmhyP8AAAAAH0EAAAAAAAB5eIgCAAAAAHl3kAIAAAAAPXEGAAAAAAC/oQAAAAAAAAcBAADI////v0IAAAAAAAC/cwAAAAAAAIUQAABp/f//eaTY/wAAAAB5odD/AAAAAHsa+P4AAAAAFQdzAAAAAAAnBwAAMAAAAL+BAAAAAAAAD3EAAAAAAAB7Ggj/AAAAAL9CAAAAAAAAJwIAADAAAAB5ofj+AAAAAA8SAAAAAAAABwIAACoAAAAFAHIAAAAAAL83AAAAAAAAJwcAADAAAAC3CQAAAQAAABgBAACRtLS0AAAAALS0tLQtcQEAAAAAALcJAAAAAAAALXECAAAAAACFEAAA1VAAAIUQAAD/////eaEA/wAAAAB5GIgCAAAAAL8hAAAAAAAAv5IAAAAAAACFEAAAkur//xUAPwAAAAAAtwYAAP////8HCAAAKAAAAHsK4P4AAAAAvwkAAAAAAAAFACgAAAAAAHmB2P8AAAAAeRIYAAAAAAB7Kvj/AAAAAHkSEAAAAAAAeyrw/wAAAAB5EggAAAAAAHsq6P8AAAAAeREAAAAAAAB7GuD/AAAAAHGDAAAAAAAAv6EAAAAAAAAHAQAAOP///7+iAAAAAAAABwIAAOD///+FEAAAT0QAAL+iAAAAAAAABwIAADj///+/kQAAAAAAALcDAAAiAAAAhRAAAG95AAAHCAAAMAAAAAcJAAAiAAAABwYAAAEAAAAHBwAA0P///1UHDwAAAAAAeago/wAAAAB5oRj/AAAAAB+BAAAAAAAAv2kAAAAAAAAHCQAAAQAAAHmnAP8AAAAAeaLg/gAAAAAtYWf/AAAAAL+hAAAAAAAABwEAABj///+/ggAAAAAAAL+TAAAAAAAAhRAAAOz8//95ouD+AAAAAAUAX/8AAAAAcYEBAAAAAABVAdb/AAAAAHmB2P8AAAAAeRIYAAAAAAB7Kvj/AAAAAHkSEAAAAAAAeyrw/wAAAAB5EggAAAAAAHsq6P8AAAAAeREAAAAAAAB7GuD/AAAAAHGDAAAAAAAAv6EAAAAAAAAHAQAAOP///7+iAAAAAAAABwIAAOD///+FEAAAMUQAAAUA1f8AAAAAeaHo/gAAAAC/kgAAAAAAAIUQAACdUAAAhRAAAP////97dgAAAAAAAFUJAQABAAAABQAmAAAAAAB5hxgAAAAAAHmJIAAAAAAAcYEoAAAAAABxgykAAAAAAHGFKgAAAAAAc1IAAAAAAABzMv//AAAAAHMS/v8AAAAAe5L2/wAAAAB7cu7/AAAAAHti5v8AAAAAe0Le/wAAAAB7Atb/AAAAAAcCAAAwAAAAeaQQ/wAAAAAHBAAAAQAAAAcIAAAwAAAAeaEI/wAAAABdGAkAAAAAAHtK2P8AAAAAeaMA/wAAAAB5NlgCAAAAAHlhAAAAAAAABwEAAAEAAAC3AgAAAQAAABUBFQAAAAAAtwIAAAAAAAAFABMAAAAAAHtKEP8AAAAAeYQIAAAAAAB5RgAAAAAAAAcGAAABAAAAtwcAAAEAAAAVBgEAAAAAALcHAAAAAAAAeYAAAAAAAAB7ZAAAAAAAAFUHAgABAAAAhRAAAP////+FEAAA/////3mGEAAAAAAAeWcAAAAAAAAHBwAAAQAAALcJAAABAAAAFQfQ/wAAAAC3CQAAAAAAAAUAzv8AAAAAeTVQAgAAAAB7FgAAAAAAAFUCAQABAAAABQDz/wAAAAB5OGACAAAAAHmBAAAAAAAABwEAAAEAAAC3AgAAAQAAABUBAQAAAAAAtwIAAAAAAAB7GAAAAAAAAFUCAQABAAAABQDq/wAAAABxMXoCAAAAAHsaCP8AAAAAcTF5AgAAAAB7GhD/AAAAAHE5eAIAAAAAeTBwAgAAAAB5N2gCAAAAAHmhyP8AAAAAXRQPAAAAAAC/oQAAAAAAAAcBAADI////v0IAAAAAAAB7euj+AAAAAL9XAAAAAAAAe5rg/gAAAAC/CQAAAAAAAIUQAABQ/P//v5AAAAAAAAB5qeD+AAAAAL91AAAAAAAAeafo/gAAAAB5odD/AAAAAHsa+P4AAAAAeaTY/wAAAAC/QQAAAAAAACcBAAAwAAAAeaP4/gAAAAC/MgAAAAAAAA8SAAAAAAAAeaEI/wAAAABzEioAAAAAAHmhEP8AAAAAcxIpAAAAAABzkigAAAAAAHsCIAAAAAAAe3IYAAAAAAB7ghAAAAAAAHtiCAAAAAAAe1IAAAAAAAAHBAAAAQAAAHtK2P8AAAAAeaYA/wAAAAB5YUgCAAAAAHliQAIAAAAAeyoA8AAAAAB7GgjwAAAAAL+hAAAAAAAABwEAABj///+/ogAAAAAAAAcCAAA4////v6UAAAAAAACFEAAA8UMAAGGhGP8AAAAAVQEEABYAAAC3AQAABAAAAHmi8P4AAAAAexIAAAAAAAAFAAwAAAAAAHmhMP8AAAAAexr4/wAAAAB5oSj/AAAAAHsa8P8AAAAAeaEg/wAAAAB7Guj/AAAAAHmhGP8AAAAAexrg/wAAAAC/ogAAAAAAAAcCAADg////eaHw/gAAAACFEAAAmjEAAL+hAAAAAAAABwEAAMj///+FEAAAAvv//3miWP8AAAAAFQIEAAAAAAAnAgAAIgAAAHmhYP8AAAAAtwMAAAEAAACFEAAAwun//3micP8AAAAAFQIDAAAAAAB5oXj/AAAAALcDAAABAAAAhRAAAL3p//95YQgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAALHp//95YRAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAKXp//95YTgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAJnp//95YUAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAI3p//95YWgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAIHp//95YXAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAHXp//95YZgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAGnp//95YaAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAF3p//95YcgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAFHp//95YdAAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAEXp//95YfgAAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAADnp//95YQABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAC3p//95YSgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAACHp//95YTABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAABXp//95YVgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAAnp//95YWABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAP3o//95YYgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAPHo//95YZABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAOXo//95YbgBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAANno//95YcABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAM3o//95YegBAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAMHo//95YfABAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAALXo//95YRgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAIAAAALcDAAAIAAAAhRAAAKno//95YSACAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRIIAAAAAAAHAgAA/////3shCAAAAAAAVQIDAAAAAAC3AgAAKAAAALcDAAAIAAAAhRAAAJ3o//+/YQAAAAAAAAcBAACAAgAAhRAAANT5//95YVgCAAAAAHkSAAAAAAAABwIAAP////97IQAAAAAAAFUCBwAAAAAAeRII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AP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACtyv//eWFoAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACAAAAC3AwAACAAAAIUQAAChyv//eWFwAAAAAAB5EgAAAAAAAAcCAAD/////eyEAAAAAAABVAgcAAAAAAHkSCAAAAAAABwIAAP////97IQgAAAAAAFUCAwAAAAAAtwIAACgAAAC3AwAACAAAAIUQAACVyv//lQAAAAAAAAC/FgAAAAAAAHlhCAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAh8r//3lhEAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAe8r//3lhOAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAb8r//3lhQAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAY8r//3lhaAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAgAAAAtwMAAAgAAACFEAAAV8r//3lhcAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAAALcCAAAoAAAAtwMAAAgAAACFEAAAS8r//3lhmAAAAAAAeRIAAAAAAAAHAgAA/////3shAAAAAAAAVQIHAAAAAAB5EggAAAAAAAcCAAD/////eyEIAAAAAABVAgMAAAAA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ALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQPJ/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQPD/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQO9/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQO3/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOx/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOr/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOl/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOf/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOZ/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOT/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQON/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOH/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQOB/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQN7/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQN1/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQNv/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQNp/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQNj/wAAAAAHAgAAAQAAALcDAAABAAAAFQIBAAAAAAC3AwAAAAAAAFcDAAABAAAAVQNd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c2FnZXMvZGVwb3NpdC5yc25vIGVudHJ5IGZvdW5kIGZvciBrZXlzcmMvdXRpbHMvYWNjb3VudHMucnMAAGFzc2VydGlvbiBmYWlsZWQ6IGlkeCA8IENBUEFDSVRZSW5zdHJ1Y3Rpb246IENvbnN1bWVQcmVwYXJlZEZpbGxBIHNpZ25lciBjb25zdHJhaW50IHdhcyB2aW9sYXRlZERlc3RpbmF0aW9uIENDVFAgZG9tYWluIG1pc21hdGNoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBbiBvd25lciBjb25zdHJhaW50IHdhcyB2aW9sYXRlZGxpYnJhcnkvY29yZS9zcmMvc2xpY2UvbWVtY2hyLnJzBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKlpbmRleCBvdXQgb2YgYm91bmRzOiB0aGUgbGVuIGlzIAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQWNjb3VudE5vdEFzc29jaWF0ZWRUb2tlbkFjY291bnRQcm9ncmFtRXJyb3IgY2F1c2VkIGJ5IGFjY291bnQ6IFRoZSBnaXZlbiBhY2NvdW50IGlzIG5vdCBtdXRhYmxlVW5rbm93biBXb3JtaG9sZUNjdHBNZXNzYWdlIHR5cGVDY3RwUmVtb3RlVG9rZW5NZXNzZW5nZXJSZXF1aXJlZHNyYy9wYXlsb2FkLnJzSW52YWxpZFNpZ25hdHVyZW5vIHN0b3JhZ2Ugc3BhY2VBbHJlYWR5IHJlZGVlbWVkUGVybWlzc2lvbkRlbmllZEludmFsaWRQcm9ncmFtSWRDcmVhdGVJZGVtcG90ZW50ZW50aXR5IG5vdCBmb3VuZGZsb2F0aW5nIHBvaW50IGBQYXllck5vdFByZXBhcmVySW5zdWZmaWNpZW50RmVlcy4gRXJyb3IgTnVtYmVyOiApIHdoZW4gc2xpY2luZyBgQWRkck5vdEF2YWlsYWJsZVJlZGVlbWVyTWlzbWF0Y2hJbW11dGFibGVQcm9ncmFtQ29uc3RyYWludEhhc09uZWludmFsaWQgZmlsZW5hbWVjb25uZWN0aW9uIHJlc2V0YWxyZWFkeSBib3Jyb3dlZG5vIGFsbG9jIGZhaWx1cmVDb25zdHJhaW50U2lnbmVySW52YWxpZFBheWxvYWRJZEFjY291bnROb3RaZXJvZWRob3N0IHVucmVhY2hhYmxlcmFuZ2UgZW5kIGluZGV4IEFjY291bnROb3RTaWduZXJzcmMvZGUvbW9kLnJzY2FsbGVkIGBPcHRpb246OnVud3JhcCgpYCBvbiBhIGBOb25lYCB2YWx1ZW1vZHVsZXMvY29tbW9uL3NyYy9hZG1pbi91dGlscy9wZW5kaW5nX293bmVyLnJzZmFpbGVkIHRvIGZpbGwgd2hvbGUgYnVmZmVyL2hvbWUvcnVubmVyL3dvcmsvcGxhdGZvcm0tdG9vbHMvcGxhdGZvcm0tdG9vbHMvb3V0L3J1c3QvbGlicmFyeS9zdGQvc3JjL2lvL2ltcGxzLnJzYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ob21lL3J1bm5lci93b3JrL3BsYXRmb3JtLXRvb2xzL3BsYXRmb3JtLXRvb2xzL291dC9ydXN0L2xpYnJhcnkvYWxsb2Mvc3JjL3N0cmluZy5yc2NhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWVJbnZhbGlkIGJvb2wgcmVwcmVzZW50YXRpb246IC9ob21lL3J1bm5lci93b3JrL3BsYXRmb3JtLXRvb2xzL3BsYXRmb3JtLXRvb2xzL291dC9ydXN0L2xpYnJhcnkvYWxsb2Mvc3JjL2NvbGxlY3Rpb25zL2J0cmVlL25vZGUucnNhc3NlcnRpb24gZmFpbGVkOiBlZGdlLmhlaWdodCA9PSBzZWxmLmhlaWdodCAtIDFhc3NlcnRpb24gZmFpbGVkOiBzcmMubGVuKCkgPT0gZHN0LmxlbigpL2hvbWUvcnVubmVyL3dvcmsvcGxhdGZvcm0tdG9vbHMvcGxhdGZvcm0tdG9vbHMvb3V0L3J1c3QvbGlicmFyeS9hbGxvYy9zcmMvY29sbGVjdGlvbnMvYnRyZWUvbmF2aWdhdGUucnMAbWludBEA//9maWxsRgAAABQA//8gICAguwsAAFNvbWUQAP//IDw9IHRydWUDAP//AQD//wABAAC6CwAArBAAAGtpbmTWBwAAY29kZXJlbnTUBwAAAgEAAKAQAABlbnVtBgIAAAACAAAFEAAAAgAAABIA//9LaW5kbGluZU5vbmUAAAAAZW5kcG9pbnROb1F1b3J1bWZpbGVuYW1lIChieXRlcyCE5Iu4cORs8PQ2Mf24LvmfyvFBum7r7lByZWRlZW1lcnNlcXVlbmNlRGVhZGxvY2tOb3RGb3VuZFRpbWVkT3V0T3ZlcmZsb3dkZWFkbG9jawMVDbanlYB2AQBJbnZhbGlkIE9wdGlvbiByZXByZXNlbnRhdGlvbjogLiBUaGUgZmlyc3QgYnl0ZSBtdXN0IGJlIDAgb3IgMXUzMiBvdmVyZmxvd3NyYy9yZWFkX3dyaXRlLnJzcHJvZ3JhbXMvdG9rZW4tcm91dGVyL3NyYy9wcm9jZXNzb3IvYWRtaW4vaW5pdGlhbGl6ZS5yc2VtaXR0ZXL/cHJvZ3JhbXMvdG9rZW4tcm91dGVyL3NyYy9wcm9jZXNzb3IvbWFya2V0X29yZGVyL3BsYWNlX2NjdHAucnNtc2djb3JlX21lc3NhZ2Vwcm9ncmFtcy90b2tlbi1yb3V0ZXIvc3JjL3Byb2Nlc3Nvci9tYXJrZXRfb3JkZXIvcHJlcGFyZS5yc3Byb2dyYW1zL3Rva2VuLXJvdXRlci9zcmMvcHJvY2Vzc29yL3JlZGVlbV9maWxsL2NjdHAucnNwcmVwYXJlZF9maWxscHJvZ3JhbXMvdG9rZW4tcm91dGVyL3NyYy9wcm9jZXNzb3IvcmVkZWVtX2ZpbGwvZmFzdC5yc3Byb2dyYW1zL3Rva2VuLXJvdXRlci9zcmMvc3RhdGUvcGF5ZXJfc2VxdWVuY2UucnNwcm9ncmFtcy90b2tlbi1yb3V0ZXIvc3JjL3N0YXRlL3ByZXBhcmVkX29yZGVyLnJzxtpUC8M2RxHRE/AOFN93Gj3pEbfsv+/63Mr4K3OqvoJJbnZhbGlkUmVkZWVtZXJJbnZhbGlkRGVwb3NpdE1lc3NhZ2VJbnZhbGlkU291cmNlUm91dGVyUmVmdW5kVG9rZW5NaXNtYXRjaE9yZGVyU2VuZGVyTWlzbWF0Y2hQcmVwYXJlZEJ5TWlzbWF0Y2hNaW5BbW91bnRPdXRUb29IaWdoSW5zdWZmaWNpZW50QW1vdW50UGF1c2VkSW52YWxpZENjdHBFbmRwb2ludEludmFsaWRNaW50UmVjaXBpZW50SW52YWxpZEVuZHBvaW50Q2hhaW5Ob3RBbGxvd2VkTm90UGVuZGluZ093bmVySW52YWxpZE5ld0Fzc2lzdGFudE5vVHJhbnNmZXJPd25lcnNoaXBSZXF1ZXN0QWxyZWFkeU93bmVyTm90VXNkY0ludmFsaWROZXdPd25lckFzc2lzdGFudFplcm9QdWJrZXlJbnZhbGlkQ3VzdG9keVRva2VuT3duZXJPckFzc2lzdGFudE9ubHlPd25lck9ubHljdXN0b2RpYW5jdXN0b2R5X3Rva2Vub3duZXJvd25lcl9hc3Npc3RhbnRwcm9ncmFtX2RhdGFzeXN0ZW1fcHJvZ3JhbXRva2VuX3Byb2dyYW1hc3NvY2lhdGVkX3Rva2VuX3Byb2dyYW1icGZfbG9hZGVyX3VwZ3JhZGVhYmxlX3Byb2dyYW1wZW5kaW5nX293bmVybmV3X293bmVyb3duZXJfb3JfYXNzaXN0YW50bmV3X293bmVyX2Fzc2lzdGFudHByZXBhcmVkX2J5cHJlcGFyZWRfb3JkZXJyZWZ1bmRfdG9rZW5vcmRlcl9zZW5kZXJyZW50X3JlY2lwaWVudGRzdF90b2tlbnNlcXBheWVyX3NlcXVlbmNlcGF5ZXJyb3V0ZXJfZW5kcG9pbnRjb3JlX2JyaWRnZV9jb25maWdjb3JlX2VtaXR0ZXJfc2VxdWVuY2Vjb3JlX2ZlZV9jb2xsZWN0b3JtZXNzYWdlX3RyYW5zbWl0dGVyX2NvbmZpZ2xvY2FsX3Rva2VuY2xvY2t0b2tlbl9tZXNzZW5nZXJfbWludGVyX3NlbmRlcl9hdXRob3JpdHl0b2tlbl9tZXNzZW5nZXJyZW1vdGVfdG9rZW5fbWVzc2VuZ2VydG9rZW5fbWludGVyY29yZV9icmlkZ2VfcHJvZ3JhbXRva2VuX21lc3Nlbmdlcl9taW50ZXJfcHJvZ3JhbW1lc3NhZ2VfdHJhbnNtaXR0ZXJfcHJvZ3JhbW9yZGVyX3Rva2VudXNlZF9ub25jZXN0b2tlbl9tZXNzZW5nZXJfbWludGVyX2N1c3RvZHlfdG9rZW52YWFtZXNzYWdlX3RyYW5zbWl0dGVyX2F1dGhvcml0eXRva2VuX3BhaXJtYXRjaGluZ19lbmdpbmVfY3VzdG9kaWFubWF0Y2hpbmdfZW5naW5lX3JlZGVlbWVkX2Zhc3RfZmlsbG1hdGNoaW5nX2VuZ2luZV9jdXN0b2R5X3Rva2VubWF0Y2hpbmdfZW5naW5lX3JvdXRlcl9lbmRwb2ludG1hdGNoaW5nX2VuZ2luZV9wcm9ncmFtcHJvZ3JhbXMvdG9rZW4tcm91dGVyL3NyYy9zdGF0ZS9jdXN0b2RpYW4ucnNDdXN0b2RpYW5QYXllclNlcXVlbmNlVW5leHBlY3RlZCB2YXJpYW50IGluZGV4OiBwcm9ncmFtcy90b2tlbi1yb3V0ZXIvc3JjL3N0YXRlL3ByZXBhcmVkX2ZpbGwucnNQcmVwYXJlZEZpbGxQcmVwYXJlZE9yZGVyDR5VoZ2Y1htPtcSxjXAHt+0fZ7NQjPd8dz9ini4CfHBJbnN0cnVjdGlvbjogUHJlcGFyZU1hcmtldE9yZGVySW5zdHJ1Y3Rpb246IENsb3NlUHJlcGFyZWRPcmRlckluc3RydWN0aW9uOiBQbGFjZU1hcmtldE9yZGVyQ2N0cEluc3RydWN0aW9uOiBSZWRlZW1DY3RwRmlsbEluc3RydWN0aW9uOiBSZWRlZW1GYXN0RmlsbEluc3RydWN0aW9uOiBJbml0aWFsaXplSW5zdHJ1Y3Rpb246IFN1Ym1pdE93bmVyc2hpcFRyYW5zZmVyUmVxdWVzdEluc3RydWN0aW9uOiBDb25maXJtT3duZXJzaGlwVHJhbnNmZXJSZXF1ZXN0SW5zdHJ1Y3Rpb246IENhbmNlbE93bmVyc2hpcFRyYW5zZmVyUmVxdWVzdEluc3RydWN0aW9uOiBVcGRhdGVPd25lckFzc2lzdGFudEluc3RydWN0aW9uOiBTZXRQYXVzZWZhaWxlZCB0byBmaWxsIHdob2xlIGJ1ZmZlcmEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvaG9tZS9ydW5uZXIvd29yay9wbGF0Zm9ybS10b29scy9wbGF0Zm9ybS10b29scy9vdXQvcnVzdC9saWJyYXJ5L2FsbG9jL3NyYy9zdHJpbmcucnNVbmV4cGVjdGVkIHZhcmlhbnQgaW5kZXg6IHByb2dyYW1zL21hdGNoaW5nLWVuZ2luZS9zcmMvc3RhdGUvcm91dGVyX2VuZHBvaW50LnJzUm91dGVyRW5kcG9pbnQAAABtb2R1bGVzL2NvbW1vbi9zcmMvbWVzc2FnZXMvZGVwb3NpdC9maWxsLnJzAAAAAABhdHRlbXB0IHRvIGFkZCB3aXRoIG92ZXJmbG93bW9kdWxlcy9jb21tb24vc3JjL21lc3NhZ2VzL3Jhdy9kZXBvc2l0LnJzVW5rbm93biBMaXF1aWRpdHlMYXllckRlcG9zaXRNZXNzYWdlIHR5cGVMaXF1aWRpdHlMYXllckRlcG9zaXRNZXNzYWdlIHNwYW4gdG9vIHNob3J0LiBOZWVkIGF0IGxlYXN0IDEgYnl0ZWNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWVGaWxsIHNwYW4gdG9vIHNob3J0LiBOZWVkIGF0IGxlYXN0IDcwIGJ5dGVzRmlsbCBwYXlsb2FkIGxlbmd0aCBtaXNtYXRjaFNsb3dPcmRlclJlc3BvbnNlIHNwYW4gdG9vIHNob3J0LiBOZWVkIGV4YWN0bHkgOCBieXRlc21vZHVsZXMvY29tbW9uL3NyYy9tZXNzYWdlcy9yYXcvbW9kLnJzTGlxdWlkaXR5TGF5ZXJNZXNzYWdlIGlzIG5vdCBGYXN0RmlsbFVua25vd24gTGlxdWlkaXR5TGF5ZXJNZXNzYWdlIHR5cGVMaXF1aWRpdHlMYXllck1lc3NhZ2Ugc3BhbiB0b28gc2hvcnQuIE5lZWQgYXQgbGVhc3QgMSBieXRlRmFzdEZpbGwgc3BhbiB0b28gc2hvcnQuIE5lZWQgYXQgbGVhc3QgNzggYnl0ZXNGYXN0TWFya2V0T3JkZXIgc3BhbiB0b28gc2hvcnQuIE5lZWQgYXQgbGVhc3QgMTM4IGJ5dGVzRmFzdE1hcmtldE9yZGVyIHBheWxvYWQgbGVuZ3RoIG1pc21hdGNoAAAAAGNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWVzcmMvaW5zdHJ1Y3Rpb24ucnNmYWlsZWQgdG8gZmlsbCB3aG9sZSBidWZmZXJhIERpc3BsYXkgaW1wbGVtZW50YXRpb24gcmV0dXJuZWQgYW4gZXJyb3IgdW5leHBlY3RlZGx5L2hvbWUvcnVubmVyL3dvcmsvcGxhdGZvcm0tdG9vbHMvcGxhdGZvcm0tdG9vbHMvb3V0L3J1c3QvbGlicmFyeS9hbGxvYy9zcmMvc3RyaW5nLnJzc3JjL2NwaS92ZXJpZnlfdmFhX2FuZF9taW50LnJzRW5jb2RlZCBtaW50IHJlY2lwaWVudCBkb2VzIG5vdCBtYXRjaCBtaW50IHJlY2lwaWVudCB0b2tlbiBhY2NvdW50Q0NUUCBub25jZSBtaXNtYXRjaFNvdXJjZSBDQ1RQIGRvbWFpbiBtaXNtYXRjaE5vdCBhIFdvcm1ob2xlIENDVFAgZGVwb3NpdCBtZXNzYWdlQ2Fubm90IHBhcnNlIGVuY29kZWQgQ0NUUCBtZXNzYWdlQ2Fubm90IHBhcnNlIFZBQSBwYXlsb2FkIGFzIFdvcm1ob2xlIENDVFAgbWVzc2FnZQAAY2FsbGVkIGBSZXN1bHQ6OnVud3JhcCgpYCBvbiBhbiBgRXJyYCB2YWx1ZWZhaWxlZCB0byBmaWxsIHdob2xlIGJ1ZmZlcmEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvaG9tZS9ydW5uZXIvd29yay9wbGF0Zm9ybS10b29scy9wbGF0Zm9ybS10b29scy9vdXQvcnVzdC9saWJyYXJ5L2FsbG9jL3NyYy9zdHJpbmcucnNVbmV4cGVjdGVkIHZhcmlhbnQgaW5kZXg6IHNyYy91dGlscy92YWEvemVyb19jb3B5L2VuY29kZWRfdmFhLnJzc3JjL3V0aWxzL3ZhYS96ZXJvX2NvcHkvcG9zdGVkX3ZhYV92MS5yc1Bvc3RlZFZBQXNyYy91dGlscy92YWEvemVyb19jb3B5L21vZC5ycysSRsnu+jxGZ5IlMRHzX+we6O5enevEEtLpra3+zcxyRXhlY3V0YWJsZURpc2FsbG93ZWRQb3N0ZWRWYWFQYXlsb2FkVG9vTGFyZ2VXcml0ZUF1dGhvcml0eU1pc21hdGNoSW52YWxpZFByb2dyYW1FbWl0dGVyRW1pdHRlckF1dGhvcml0eU1pc21hdGNoQ2Fubm90UGFyc2VWYWFFeGNlZWRzTWF4UGF5bG9hZFNpemVEYXRhT3ZlcmZsb3dJbnZhbGlkQ3JlYXRlZEFjY291bnRTaXplSW52YWxpZFZhYVZlcnNpb25IYXNoTm90Q29tcHV0ZWRJbnZhbGlkTWVzc2FnZVN0YXR1c05vdEluV3JpdGluZ1N0YXR1c0luV3JpdGluZ1N0YXR1c1ZhYVN0aWxsUHJvY2Vzc2luZ1VudmVyaWZpZWRWYWFJbnZhbGlkR3VhcmRpYW5JbmRleFZhYUFscmVhZHlWZXJpZmllZFZhYVdyaXRpbmdEaXNhbGxvd2VkTWVzc2FnZUFscmVhZHlQdWJsaXNoZWREdXBsaWNhdGVHdWFyZGlhbkFkZHJlc3NHdWFyZGlhblplcm9BZGRyZXNzWmVyb0d1YXJkaWFuc1BheWxvYWRTaXplTWlzbWF0Y2hTaWduZXJJbmRpY2VzTWlzbWF0Y2hJbnZhbGlkR3VhcmRpYW5LZXlSZWNvdmVyeUd1YXJkaWFuU2V0RXhwaXJlZEludmFsaWRTaWdWZXJpZnlJbnN0cnVjdGlvbkVtcHR5U2lnVmVyaWZ5SW5zdHJ1Y3Rpb25JbnN0cnVjdGlvbkF0V3JvbmdJbmRleEd1YXJkaWFuU2V0TWlzbWF0Y2hJbnZhbGlkR3VhcmRpYW5TZXRJbmRleEltcGxlbWVudGF0aW9uTWlzbWF0Y2hJbnZhbGlkRmVlUmVjaXBpZW50Tm90RW5vdWdoTGFtcG9ydHNNZXNzYWdlTWlzbWF0Y2hJbnZhbGlkTWVzc2FnZUhhc2hJbnZhbGlkU2lnbmF0dXJlU2V0TGVnYWN5RW1pdHRlckV4ZWN1dGFibGVFbWl0dGVySW52YWxpZFByZXBhcmVkTWVzc2FnZU5vdFJlYWR5Rm9yUHVibGlzaGluZ0VtaXR0ZXJNaXNtYXRjaEludmFsaWRHb3Zlcm5hbmNlVmFhR292ZXJuYW5jZUZvckFub3RoZXJDaGFpbkxhdGVzdEd1YXJkaWFuU2V0UmVxdWlyZWRJbnZhbGlkR292ZXJuYW5jZUFjdGlvbkludmFsaWRHb3Zlcm5hbmNlRW1pdHRlckludmFsaWRDaGFpbkludmFsaWRDb21wdXRlU2l6ZVU2NE92ZXJmbG93SW52YWxpZERhdGFDb252ZXJzaW9uSW52YWxpZEluc3RydWN0aW9uQXJndW1lbnRFeGNlZWRzTWF4UGF5bG9hZFNpemUgKDMwS0IpAAAAAHNyYy9wcm90b2NvbC5yc0hlYWRlcjogaW52YWxpZCBsZW5ndGguIEV4cGVjdGVkIGF0IGxlYXN0IDYgYnl0ZXMuSGVhZGVyOiBJbnN1ZmZpY2llbnQgYnl0ZXMgdG8gcGFyc2UgYWxsIHNpZ25hdHVyZXNCb2R5OiBpbnZhbGlkIGxlbmd0aC4gRXhwZWN0ZWQgYXQgbGVhc3QgNTEgYnl0ZXMuV29ybWhvbGVDY3RwUGF5bG9hZCBzcGFuIHRvbyBzaG9ydC4gTmVlZCBhdCBsZWFzdCAxIGJ5dGVXb3JtaG9sZUNjdHBNZXNzYWdlIGlzIG5vdCBEZXBvc2l0c3JjL3BheWxvYWRzL2NjdHAvcGF5bG9hZHMucnNXb3JtaG9sZUNjdHBNZXNzYWdlIHNwYW4gdG9vIHNob3J0LiBOZWVkIGF0IGxlYXN0IDEgYnl0ZURlcG9zaXQgc3BhbiB0b28gc2hvcnQuIE5lZWQgYXQgbGVhc3QgMTQ2IGJ5dGVzRGVwb3NpdCBwYXlsb2FkIGxlbmd0aCBtaXNtYXRjaHNyYy9yZWFkX3dyaXRlLnJzAAAAAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvdwAAAABhc3NlcnRpb24gZmFpbGVkOiBgYCBkb2VzIG5vdCBtYXRjaCBgYEFzc29jaWF0ZWRUb2tlbkFjY291bnRJbnN0cnVjdGlvbjo6Q3JlYXRlIHwKQXNzb2NpYXRlZFRva2VuQWNjb3VudEluc3RydWN0aW9uOjpDcmVhdGVJZGVtcG90ZW50c3JjL2luc3RydWN0aW9uLnJzUmVjb3Zlck5lc3RlZENyZWF0ZQAAAAAAAGF0dGVtcHQgdG8gYWRkIHdpdGggb3ZlcmZsb3dzcmMvaW5zdHJ1Y3Rpb24ucnNzcmMvc3RhdGUucnN2YXJpYW50IGluZGV4IDAgPD0gaSA8IDRhIERpc3BsYXkgaW1wbGVtZW50YXRpb24gcmV0dXJuZWQgYW4gZXJyb3IgdW5leHBlY3RlZGx5L2hvbWUvcnVubmVyL3dvcmsvcGxhdGZvcm0tdG9vbHMvcGxhdGZvcm0tdG9vbHMvb3V0L3J1c3QvbGlicmFyeS9hbGxvYy9zcmMvc3RyaW5nLnJzAAAAaW52YWxpZCB2YWx1ZTogLCBleHBlY3RlZCAAAAAAAABhdHRlbXB0IHRvIGFkZCB3aXRoIG92ZXJmbG93c3JjL2JwZl93cml0ZXIucnNzcmMvY29tbW9uLnJzY2FsbGVkIGBPcHRpb246OnVud3JhcCgpYCBvbiBhIGBOb25lYCB2YWx1ZS4gRXJyb3IgQ29kZTogLiBFcnJvciBNZXNzYWdlOiAuUHJvZ3JhbUVycm9yIHRocm93biBpbiA6UHJvZ3JhbUVycm9yIG9jY3VycmVkLiBFcnJvciBDb2RlOiBMZWZ0OlJpZ2h0OkxlZnQ6IFJpZ2h0OiBBbmNob3JFcnJvciBjYXVzZWQgYnkgYWNjb3VudDogQW5jaG9yRXJyb3IgdGhyb3duIGluIEFuY2hvckVycm9yIG9jY3VycmVkLiBFcnJvciBDb2RlOiBEZXByZWNhdGVkVHJ5aW5nVG9Jbml0UGF5ZXJBc1Byb2dyYW1BY2NvdW50RGVjbGFyZWRQcm9ncmFtSWRNaXNtYXRjaEFjY291bnREdXBsaWNhdGVSZWFsbG9jc0FjY291bnRSZWFsbG9jRXhjZWVkc0xpbWl0QWNjb3VudFN5c3Zhck1pc21hdGNoQWNjb3VudE5vdFByb2dyYW1EYXRhQWNjb3VudE5vdEluaXRpYWxpemVkQWNjb3VudE5vdFN5c3RlbU93bmVkSW52YWxpZFByb2dyYW1FeGVjdXRhYmxlQWNjb3VudE93bmVkQnlXcm9uZ1Byb2dyYW1BY2NvdW50Tm90TXV0YWJsZUFjY291bnROb3RFbm91Z2hLZXlzQWNjb3VudERpZE5vdFNlcmlhbGl6ZUFjY291bnREaWROb3REZXNlcmlhbGl6ZUFjY291bnREaXNjcmltaW5hdG9yTWlzbWF0Y2hBY2NvdW50RGlzY3JpbWluYXRvck5vdEZvdW5kQWNjb3VudERpc2NyaW1pbmF0b3JBbHJlYWR5U2V0UmVxdWlyZUd0ZVZpb2xhdGVkUmVxdWlyZUd0VmlvbGF0ZWRSZXF1aXJlS2V5c05lcVZpb2xhdGVkUmVxdWlyZU5lcVZpb2xhdGVkUmVxdWlyZUtleXNFcVZpb2xhdGVkUmVxdWlyZUVxVmlvbGF0ZWRSZXF1aXJlVmlvbGF0ZWRDb25zdHJhaW50QXNzb2NpYXRlZFRva2VuVG9rZW5Qcm9ncmFtQ29uc3RyYWludE1pbnRUb2tlblByb2dyYW1Db25zdHJhaW50VG9rZW5Ub2tlblByb2dyYW1Db25zdHJhaW50QWNjb3VudElzTm9uZUNvbnN0cmFpbnRTcGFjZUNvbnN0cmFpbnRNaW50RGVjaW1hbHNDb25zdHJhaW50TWludEZyZWV6ZUF1dGhvcml0eUNvbnN0cmFpbnRNaW50TWludEF1dGhvcml0eUNvbnN0cmFpbnRUb2tlbk93bmVyQ29uc3RyYWludFRva2VuTWludENvbnN0cmFpbnRaZXJvQ29uc3RyYWludEFkZHJlc3NDb25zdHJhaW50Q2xvc2VDb25zdHJhaW50QXNzb2NpYXRlZEluaXRDb25zdHJhaW50QXNzb2NpYXRlZENvbnN0cmFpbnRTdGF0ZUNvbnN0cmFpbnRFeGVjdXRhYmxlQ29uc3RyYWludFNlZWRzQ29uc3RyYWludFJlbnRFeGVtcHRDb25zdHJhaW50T3duZXJDb25zdHJhaW50UmF3Q29uc3RyYWludE11dEV2ZW50SW5zdHJ1Y3Rpb25TdHViSWRsQWNjb3VudE5vdEVtcHR5SWRsSW5zdHJ1Y3Rpb25JbnZhbGlkUHJvZ3JhbUlkbEluc3RydWN0aW9uU3R1Ykluc3RydWN0aW9uRGlkTm90U2VyaWFsaXplSW5zdHJ1Y3Rpb25EaWROb3REZXNlcmlhbGl6ZUluc3RydWN0aW9uRmFsbGJhY2tOb3RGb3VuZEluc3RydWN0aW9uTWlzc2luZ1RoZSBBUEkgYmVpbmcgdXNlZCBpcyBkZXByZWNhdGVkIGFuZCBzaG91bGQgbm8gbG9uZ2VyIGJlIHVzZWRZb3UgY2Fubm90L3Nob3VsZCBub3QgaW5pdGlhbGl6ZSB0aGUgcGF5ZXIgYWNjb3VudCBhcyBhIHByb2dyYW0gYWNjb3VudFRoZSBkZWNsYXJlZCBwcm9ncmFtIGlkIGRvZXMgbm90IG1hdGNoIHRoZSBhY3R1YWwgcHJvZ3JhbSBpZFRoZSBhY2NvdW50IHdhcyBkdXBsaWNhdGVkIGZvciBtb3JlIHRoYW4gb25lIHJlYWxsb2NhdGlvblRoZSBhY2NvdW50IHJlYWxsb2NhdGlvbiBleGNlZWRzIHRoZSBNQVhfUEVSTUlUVEVEX0RBVEFfSU5DUkVBU0UgbGltaXRUaGUgZ2l2ZW4gcHVibGljIGtleSBkb2VzIG5vdCBtYXRjaCB0aGUgcmVxdWlyZWQgc3lzdmFyVGhlIGdpdmVuIGFjY291bnQgaXMgbm90IHRoZSBhc3NvY2lhdGVkIHRva2VuIGFjY291bnRUaGUgZ2l2ZW4gYWNjb3VudCBpcyBub3QgYSBwcm9ncmFtIGRhdGEgYWNjb3VudFRoZSBwcm9ncmFtIGV4cGVjdGVkIHRoaXMgYWNjb3VudCB0byBiZSBhbHJlYWR5IGluaXRpYWxpemVkVGhlIGdpdmVuIGFjY291bnQgaXMgbm90IG93bmVkIGJ5IHRoZSBzeXN0ZW0gcHJvZ3JhbVRoZSBnaXZlbiBhY2NvdW50IGRpZCBub3Qgc2lnblByb2dyYW0gYWNjb3VudCBpcyBub3QgZXhlY3V0YWJsZVByb2dyYW0gSUQgd2FzIG5vdCBhcyBleHBlY3RlZFRoZSBnaXZlbiBhY2NvdW50IGlzIG93bmVkIGJ5IGEgZGlmZmVyZW50IHByb2dyYW0gdGhhbiBleHBlY3RlZE5vdCBlbm91Z2ggYWNjb3VudCBrZXlzIGdpdmVuIHRvIHRoZSBpbnN0cnVjdGlvbkZhaWxlZCB0byBzZXJpYWxpemUgdGhlIGFjY291bnRGYWlsZWQgdG8gZGVzZXJpYWxpemUgdGhlIGFjY291bnQ4IGJ5dGUgZGlzY3JpbWluYXRvciBkaWQgbm90IG1hdGNoIHdoYXQgd2FzIGV4cGVjdGVkTm8gOCBieXRlIGRpc2NyaW1pbmF0b3Igd2FzIGZvdW5kIG9uIHRoZSBhY2NvdW50VGhlIGFjY291bnQgZGlzY3JpbWluYXRvciB3YXMgYWxyZWFkeSBzZXQgb24gdGhpcyBhY2NvdW50QSByZXF1aXJlX2d0ZSBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEEgcmVxdWlyZV9ndCBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEEgcmVxdWlyZV9rZXlzX25lcSBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEEgcmVxdWlyZV9uZXEgZXhwcmVzc2lvbiB3YXMgdmlvbGF0ZWRBIHJlcXVpcmVfa2V5c19lcSBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEEgcmVxdWlyZV9lcSBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEEgcmVxdWlyZSBleHByZXNzaW9uIHdhcyB2aW9sYXRlZEFuIGFzc29jaWF0ZWQgdG9rZW4gYWNjb3VudCB0b2tlbiBwcm9ncmFtIGNvbnN0cmFpbnQgd2FzIHZpb2xhdGVkQSBtaW50IHRva2VuIHByb2dyYW0gY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBIHRva2VuIGFjY291bnQgdG9rZW4gcHJvZ3JhbSBjb25zdHJhaW50IHdhcyB2aW9sYXRlZEEgcmVxdWlyZWQgYWNjb3VudCBmb3IgdGhlIGNvbnN0cmFpbnQgaXMgTm9uZUEgc3BhY2UgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBIG1pbnQgZGVjaW1hbHMgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBIG1pbnQgZnJlZXplIGF1dGhvcml0eSBjb25zdHJhaW50IHdhcyB2aW9sYXRlZEEgbWludCBtaW50IGF1dGhvcml0eSBjb25zdHJhaW50IHdhcyB2aW9sYXRlZEEgdG9rZW4gb3duZXIgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBIHRva2VuIG1pbnQgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRFeHBlY3RlZCB6ZXJvIGFjY291bnQgZGlzY3JpbWluYW50QW4gYWRkcmVzcyBjb25zdHJhaW50IHdhcyB2aW9sYXRlZEEgY2xvc2UgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBbiBhc3NvY2lhdGVkIGluaXQgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBbiBhc3NvY2lhdGVkIGNvbnN0cmFpbnQgd2FzIHZpb2xhdGVkRGVwcmVjYXRlZCBFcnJvciwgZmVlbCBmcmVlIHRvIHJlcGxhY2Ugd2l0aCBzb21ldGhpbmcgZWxzZUFuIGV4ZWN1dGFibGUgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRBIHNlZWRzIGNvbnN0cmFpbnQgd2FzIHZpb2xhdGVkQSByZW50IGV4ZW1wdGlvbiBjb25zdHJhaW50IHdhcyB2aW9sYXRlZEEgcmF3IGNvbnN0cmFpbnQgd2FzIHZpb2xhdGVkQSBoYXMgb25lIGNvbnN0cmFpbnQgd2FzIHZpb2xhdGVkQSBtdXQgY29uc3RyYWludCB3YXMgdmlvbGF0ZWRUaGUgcHJvZ3JhbSB3YXMgY29tcGlsZWQgd2l0aG91dCBgZXZlbnQtY3BpYCBmZWF0dXJlSURMIGFjY291bnQgbXVzdCBiZSBlbXB0eSBpbiBvcmRlciB0byByZXNpemUsIHRyeSBjbG9zaW5nIGZpcnN0SW52YWxpZCBwcm9ncmFtIGdpdmVuIHRvIHRoZSBJREwgaW5zdHJ1Y3Rpb25UaGUgcHJvZ3JhbSB3YXMgY29tcGlsZWQgd2l0aG91dCBpZGwgaW5zdHJ1Y3Rpb25zVGhlIHByb2dyYW0gY291bGQgbm90IHNlcmlhbGl6ZSB0aGUgZ2l2ZW4gaW5zdHJ1Y3Rpb25UaGUgcHJvZ3JhbSBjb3VsZCBub3QgZGVzZXJpYWxpemUgdGhlIGdpdmVuIGluc3RydWN0aW9uRmFsbGJhY2sgZnVuY3Rpb25zIGFyZSBub3Qgc3VwcG9ydGVkOCBieXRlIGluc3RydWN0aW9uIGlkZW50aWZpZXIgbm90IHByb3ZpZGVkUHJvZ3JhbUVycm9yQW5jaG9yRXJyb3JQcm9ncmFtRXJyb3JXaXRoT3JpZ2lucHJvZ3JhbV9lcnJvcmVycm9yX29yaWdpbmNvbXBhcmVkX3ZhbHVlc1B1YmtleXNWYWx1ZXNBY2NvdW50TmFtZVNvdXJjZWVycm9yX25hbWVlcnJvcl9jb2RlX251bWJlcmVycm9yX21zZwBhdHRlbXB0IHRvIGFkZCB3aXRoIG92ZXJmbG93AAAAAGF0dGVtcHQgdG8gbXVsdGlwbHkgd2l0aCBvdmVyZmxvd/////////////////////////////////////////////////////////////////8AAQIDBAUGBwj/////////CQoLDA0ODxD/ERITFBX/FhcYGRobHB0eHyD///////8hIiMkJSYnKCkqK/8sLS4vMDEyMzQ1Njc4Of//////MTIzNDU2Nzg5QUJDREVGR0hKS0xNTlBRUlNUVVZXWFlaYWJjZGVmZ2hpamttbm9wcXJzdHV2d3h5enNyYy9lbmNvZGUucnNjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlYWxyZWFkeSBtdXRhYmx5IGJvcnJvd2Vkc3JjL3Nlci9tb2QucnNzcmMvYWNjb3VudF9pbmZvLnJzc3JjL2VudHJ5cG9pbnQucnNzcmMvaW5zdHJ1Y3Rpb24ucnNVbnN1cHBvcnRlZCBQdWJrZXlFcnJvcnNyYy9wdWJrZXkucnNVbmFibGUgdG8gZmluZCBhIHZpYWJsZSBwcm9ncmFtIGFkZHJlc3MgYnVtcCBzZWVkc3JjL3JlbnQucnNCdWlsdGluUHJvZ3JhbXNNdXN0Q29uc3VtZUNvbXB1dGVVbml0c01heEluc3RydWN0aW9uVHJhY2VMZW5ndGhFeGNlZWRlZE1heEFjY291bnRzRGF0YUFsbG9jYXRpb25zRXhjZWVkZWRJbGxlZ2FsT3duZXJVbnN1cHBvcnRlZFN5c3ZhckFjY291bnROb3RSZW50RXhlbXB0Qm9yc2hJb0Vycm9ySW52YWxpZFJlYWxsb2NJbnZhbGlkU2VlZHNNYXhTZWVkTGVuZ3RoRXhjZWVkZWRDdXN0b21BY2NvdW50Qm9ycm93RmFpbGVkTm90RW5vdWdoQWNjb3VudEtleXNVbmluaXRpYWxpemVkQWNjb3VudEFjY291bnRBbHJlYWR5SW5pdGlhbGl6ZWRNaXNzaW5nUmVxdWlyZWRTaWduYXR1cmVJbmNvcnJlY3RQcm9ncmFtSWRJbnN1ZmZpY2llbnRGdW5kc0FjY291bnREYXRhVG9vU21hbGxJbnZhbGlkQWNjb3VudERhdGFJbnZhbGlkSW5zdHJ1Y3Rpb25EYXRhSW52YWxpZEFyZ3VtZW50QnVpbHRpbiBwcm9ncmFtcyBtdXN0IGNvbnN1bWUgY29tcHV0ZSB1bml0c0FjY291bnRzIGRhdGEgYWxsb2NhdGlvbnMgZXhjZWVkZWQgdGhlIG1heGltdW0gYWxsb3dlZCBwZXIgdHJhbnNhY3Rpb25Qcm92aWRlZCBvd25lciBpcyBub3QgYWxsb3dlZFVuc3VwcG9ydGVkIHN5c3ZhckFuIGFjY291bnQgZG9lcyBub3QgaGF2ZSBlbm91Z2ggbGFtcG9ydHMgdG8gYmUgcmVudC1leGVtcHRQcm92aWRlZCBzZWVkcyBkbyBub3QgcmVzdWx0IGluIGEgdmFsaWQgYWRkcmVzc0xlbmd0aCBvZiB0aGUgc2VlZCBpcyB0b28gbG9uZyBmb3IgYWRkcmVzcyBnZW5lcmF0aW9uAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAABAAAACAAAAADAAAAAAAAAAAAAAAAAAAASW5zdHJ1Y3Rpb24gdHJhY2UgbGVuZ3RoIGV4Y2VlZGVkIHRoZSBtYXhpbXVtIGFsbG93ZWQgcGVyIHRyYW5zYWN0aW9uQWNjb3VudCBkYXRhIHJlYWxsb2NhdGlvbiB3YXMgaW52YWxpZElPIEVycm9yOiBGYWlsZWQgdG8gYm9ycm93IGEgcmVmZXJlbmNlIHRvIGFjY291bnQgZGF0YSwgYWxyZWFkeSBib3Jyb3dlZFRoZSBpbnN0cnVjdGlvbiBleHBlY3RlZCBhZGRpdGlvbmFsIGFjY291bnQga2V5c0FuIGF0dGVtcHQgdG8gb3BlcmF0ZSBvbiBhbiBhY2NvdW50IHRoYXQgaGFzbid0IGJlZW4gaW5pdGlhbGl6ZWRBbiBpbml0aWFsaXplIGluc3RydWN0aW9uIHdhcyBzZW50IHRvIGFuIGFjY291bnQgdGhhdCBoYXMgYWxyZWFkeSBiZWVuIGluaXRpYWxpemVkQSBzaWduYXR1cmUgd2FzIHJlcXVpcmVkIGJ1dCBub3QgZm91bmRUaGUgYWNjb3VudCBkaWQgbm90IGhhdmUgdGhlIGV4cGVjdGVkIHByb2dyYW0gaWRBbiBhY2NvdW50J3MgYmFsYW5jZSB3YXMgdG9vIHNtYWxsIHRvIGNvbXBsZXRlIHRoZSBpbnN0cnVjdGlvbkFuIGFjY291bnQncyBkYXRhIHdhcyB0b28gc21hbGxBbiBhY2NvdW50J3MgZGF0YSBjb250ZW50cyB3YXMgaW52YWxpZEFuIGluc3RydWN0aW9uJ3MgZGF0YSBjb250ZW50cyB3YXMgaW52YWxpZFRoZSBhcmd1bWVudHMgcHJvdmlkZWQgdG8gYSBwcm9ncmFtIGluc3RydWN0aW9uIHdlcmUgaW52YWxpZEN1c3RvbSBwcm9ncmFtIGVycm9yOiBCdWZmZXJUb29TbWFsbEN1c3RvbVNlcXVlbmNlTXVzdEhhdmVMZW5ndGhTaXplTGltaXREZXNlcmlhbGl6ZUFueU5vdFN1cHBvcnRlZEludmFsaWRUYWdFbmNvZGluZ0ludmFsaWRDaGFyRW5jb2RpbmdJbnZhbGlkQm9vbEVuY29kaW5nSW52YWxpZFV0ZjhFbmNvZGluZ0lvAAAAAABieXRlIGFycmF5c3RydWN0IHZhcmlhbnR0dXBsZSB2YXJpYW50bmV3dHlwZSB2YXJpYW50dW5pdCB2YXJpYW50bWFwbmV3dHlwZSBzdHJ1Y3RPcHRpb24gdmFsdWV1bml0IHZhbHVlc3RyaW5nIGNoYXJhY3RlciBgYGludGVnZXIgYGJvb2xlYW4gYAAAAAB1bmNhdGVnb3JpemVkIGVycm9yb3RoZXIgZXJyb3JvdXQgb2YgbWVtb3J5dW5leHBlY3RlZCBlbmQgb2YgZmlsZXVuc3VwcG9ydGVkb3BlcmF0aW9uIGludGVycnVwdGVkYXJndW1lbnQgbGlzdCB0b28gbG9uZ3RvbyBtYW55IGxpbmtzY3Jvc3MtZGV2aWNlIGxpbmsgb3IgcmVuYW1lZXhlY3V0YWJsZSBmaWxlIGJ1c3lyZXNvdXJjZSBidXN5ZmlsZSB0b28gbGFyZ2VmaWxlc3lzdGVtIHF1b3RhIGV4Y2VlZGVkc2VlayBvbiB1bnNlZWthYmxlIGZpbGV3cml0ZSB6ZXJvdGltZWQgb3V0aW52YWxpZCBkYXRhaW52YWxpZCBpbnB1dCBwYXJhbWV0ZXJzdGFsZSBuZXR3b3JrIGZpbGUgaGFuZGxlZmlsZXN5c3RlbSBsb29wIG9yIGluZGlyZWN0aW9uIGxpbWl0IChlLmcuIHN5bWxpbmsgbG9vcClyZWFkLW9ubHkgZmlsZXN5c3RlbSBvciBzdG9yYWdlIG1lZGl1bWRpcmVjdG9yeSBub3QgZW1wdHlpcyBhIGRpcmVjdG9yeW5vdCBhIGRpcmVjdG9yeW9wZXJhdGlvbiB3b3VsZCBibG9ja2VudGl0eSBhbHJlYWR5IGV4aXN0c2Jyb2tlbiBwaXBlbmV0d29yayBkb3duYWRkcmVzcyBub3QgYXZhaWxhYmxlYWRkcmVzcyBpbiB1c2Vub3QgY29ubmVjdGVkY29ubmVjdGlvbiBhYm9ydGVkbmV0d29yayB1bnJlYWNoYWJsZWNvbm5lY3Rpb24gcmVmdXNlZHBlcm1pc3Npb24gZGVuaWVkRXJyb3JtZXNzYWdlT3MgKG9zIGVycm9yIClVbnN1cHBvcnRlZEN1c3RvbWVycm9yVW5jYXRlZ29yaXplZE90aGVyT3V0T2ZNZW1vcnlVbmV4cGVjdGVkRW9mSW50ZXJydXB0ZWRBcmd1bWVudExpc3RUb29Mb25nSW52YWxpZEZpbGVuYW1lVG9vTWFueUxpbmtzQ3Jvc3Nlc0RldmljZXNFeGVjdXRhYmxlRmlsZUJ1c3lSZXNvdXJjZUJ1c3lGaWxlVG9vTGFyZ2VGaWxlc3lzdGVtUXVvdGFFeGNlZWRlZE5vdFNlZWthYmxlU3RvcmFnZUZ1bGxXcml0ZVplcm9JbnZhbGlkRGF0YUludmFsaWRJbnB1dFN0YWxlTmV0d29ya0ZpbGVIYW5kbGVGaWxlc3lzdGVtTG9vcFJlYWRPbmx5RmlsZXN5c3RlbURpcmVjdG9yeU5vdEVtcHR5SXNBRGlyZWN0b3J5Tm90QURpcmVjdG9yeVdvdWxkQmxvY2tBbHJlYWR5RXhpc3RzQnJva2VuUGlwZU5ldHdvcmtEb3duQWRkckluVXNlTm90Q29ubmVjdGVkQ29ubmVjdGlvbkFib3J0ZWROZXR3b3JrVW5yZWFjaGFibGVIb3N0VW5yZWFjaGFibGVDb25uZWN0aW9uUmVzZXRDb25uZWN0aW9uUmVmdXNlZEVycm9yOiBtZW1vcnkgYWxsb2NhdGlvbiBmYWlsZWQsIG91dCBvZiBtZW1vcnkAAGxpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnNjYXBhY2l0eSBvdmVyZmxvd2EgZm9ybWF0dGluZyB0cmFpdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvcmxpYnJhcnkvYWxsb2Mvc3JjL2ZtdC5yc2J5dGVzZXJyb3JGcm9tVXRmOEVycm9yAGFzc2VydGlvbiBmYWlsZWQ6IGVkZWx0YSA+PSAwbGlicmFyeS9jb3JlL3NyYy9udW0vZGl5X2Zsb2F0LnJzAAABAAAACgAAAGQAAADoAwAAECcAAKCGAQBAQg8AgJaYAADh9QUAypo7AgAAABQAAADIAAAA0AcAACBOAABADQMAgIQeAAAtMQEAwusLAJQ1dwAAwW/yhiMAAAAAAIHvrIVbQW0t7gQAAAAAAAAAAAAAAR9qv2TtOG7tl6fa9Pk/6QNPGAAAAAAAAAAAAAAAAAAAAAAAAT6VLgmZ3wP9OBUPL+R0I+z1z9MI3ATE2rDNvBl/M6YDJh/pTgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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ZmFpbGVkOiAhYnVmLmlzX2VtcHR5KCljYWxsZWQgYE9wdGlvbjo6dW53cmFwKClgIG9uIGEgYE5vbmVgIHZhbHVlYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50IDwgKDEgPDwgNjEpbGlicmFyeS9jb3JlL3NyYy9udW0vZmx0MmRlYy9tb2QucnNhc3NlcnRpb24gZmFpbGVkOiBidWZbMF0gPiBiXCcwXCdhc3NlcnRpb24gZmFpbGVkOiBwYXJ0cy5sZW4oKSA+PSA0MC4uLSswaW5mTmFOYXNzZXJ0aW9uIGZhaWxlZDogYnVmLmxlbigpID49IG1heGxlbikuLkJvcnJvd0Vycm9yQm9ycm93TXV0RXJyb3IgYnV0IHRoZSBpbmRleCBpcyA6cGFuaWNrZWQgYXQgJycsIG1hdGNoZXMhPT09YXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ICByaWdodClgCiAgbGVmdDogYGAsCiByaWdodDogYGA6IGA6ICB7CiwKLCAgeyB9IH0oCigsCltdbGlicmFyeS9jb3JlL3NyYy9mbXQvbnVtLnJzMHgwMDAxMDIwMzA0MDUwNjA3MDgwOTEwMTExMjEzMTQxNTE2MTcxODE5MjAyMTIyMjMyNDI1MjYyNzI4MjkzMDMxMzIzMzM0MzUzNjM3MzgzOTQwNDE0MjQzNDQ0NTQ2NDc0ODQ5NTA1MTUyNTM1NDU1NTY1NzU4NTk2MDYxNjI2MzY0NjU2NjY3Njg2OTcwNzE3MjczNzQ3NTc2Nzc3ODc5ODA4MTgyODM4NDg1ODY4Nzg4ODk5MDkxOTI5Mzk0OTU5Njk3OTg5OWxpYnJhcnkvY29yZS9zcmMvZm10L21vZC5yczAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBmYWxzZSgpcmFuZ2Ugc3RhcnQgaW5kZXggIG91dCBvZiByYW5nZSBmb3Igc2xpY2Ugb2YgbGVuZ3RoIHNsaWNlIGluZGV4IHN0YXJ0cyBhdCAgYnV0IGVuZHMgYXQgbGlicmFyeS9jb3JlL3NyYy9zdHIvdmFsaWRhdGlvbnMucnMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB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AAAAAADAcwgAAAAAAEUFCgAhAAAAAAAAAEwAAAAJAAAAAAAAAEUFCgAhAAAAAAAAAE4AAAAJAAAAAAAAAJAGCgAvAAAAAAAAAEMAAAAVAAAAAAAAAJAGCgAvAAAAAAAAAHUAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAHYAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAHcAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAHgAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAHkAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAHoAAAAFAAAAAAAAAJAGCgAvAAAAAAAAAMEAAAAJAAAAAAAAAJAGCgAvAAAAAAAAAPkAAABUAAAAAAAAAJAGCgAvAAAAAAAAAPoAAAANAAAAAAAAAJAGCgAvAAAAAAAAAAEBAAAzAAAAAAAAAJAGCgAvAAAAAAAAAAoBAAAFAAAAAAAAAJAGCgAvAAAAAAAAAAsBAAAFAAAAAAAAAJAGCgAvAAAAAAAAAAwBAAAFAAAAAAAAAJAGCgAvAAAAAAAAAA0BAAAFAAAAAAAAAJAGCgAvAAAAAAAAAA4BAAAFAAAAAAAAAJAGCgAvAAAAAAAAAEsBAAAfAAAAAAAAAJAGCgAvAAAAAAAAAGUBAAANAAAAAAAAAJAGCgAvAAAAAAAAAHEBAAAkAAAAAAAAAJAGCgAvAAAAAAAAAHYBAABUAAAAAAAAAJAGCgAvAAAAAAAAAIMBAAAzAAAAAAAAAMAMCgAuAAAAAAAAAH0AAAAVAAAAAAAAAMAMCgAuAAAAAAAAAKkAAAAFAAAAAAAAAMAMCgAuAAAAAAAAAKoAAAAFAAAAAAAAAMAMCgAuAAAAAAAAAKsAAAAFAAAAAAAAAMAMCgAuAAAAAAAAAKwAAAAFAAAAAAAAAMAMCgAuAAAAAAAAAK0AAAAFAAAAAAAAAMAMCgAuAAAAAAAAAK4AAAAFAAAAAAAAAMAMCgAuAAAAAAAAAK8AAAAFAAAAAAAAAMAMCgAuAAAAAAAAAAoBAAARAAAAAAAAAMAMCgAuAAAAAAAAAA0BAAAJAAAAAAAAAMAMCgAuAAAAAAAAABYBAABCAAAAAAAAAMAMCgAuAAAAAAAAAEABAAAJAAAAAAAAAMAMCgAuAAAAAAAAANwBAAAFAAAAAAAAAMAMCgAuAAAAAAAAAN0BAAAFAAAAAAAA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ABjKBgAAAAAACAAAAAAAAACwzAYAAAAAAAgAAAAAAAAA+MwGAAAAAAAIAAAAAAAAADjNBgAAAAAACAAAAAAAAAB4zQYAAAAAAAgAAAAAAAAAsM0GAAAAAAAIAAAAAAAAAOjNBgAAAAAACAAAAAAAAAAgzgYAAAAAAAgAAAAAAAAAWM4GAAAAAAAIAAAAAAAAAIjOBgAAAAAACAAAAAAAAAC4zgYAAAAAAAgAAAAAAAAA6M4GAAAAAAAIAAAAAAAAABjPBgAAAAAACAAAAAAAAABIzwYAAAAAAAgAAAAAAAAAeM8GAAAAAAAIAAAAAAAAAKjPBgAAAAAACAAAAAAAAADYzwYAAAAAAAgAAAAAAAAAANAGAAAAAAAIAAAAAAAAACDQBgAAAAAACAAAAAAAAABI0AYAAAAAAAgAAAAAAAAAcNAGAAAAAAAIAAAAAAAAAJjQBgAAAAAACAAAAAAAAADA0AYAAAAAAAgAAAAAAAAA4NAGAAAAAAAIAAAAAAAAAAjRBgAAAAAACAAAAAAAAAAw0QYAAAAAAAgAAAAAAAAAUNEGAAAAAAAIAAAAAAAAAHDRBgAAAAAACAAAAAAAAACQ0QYAAAAAAAgAAAAAAAAAsNEGAAAAAAAIAAAAAAAAANDRBgAAAAAACAAAAAAAAADw0QYAAAAAAAgAAAAAAAAAENIGAAAAAAAIAAAAAAAAADDSBgAAAAAACAAAAAAAAABQ0gYAAAAAAAgAAAAAAAAAcNIGAAAAAAAIAAAAAAAAAIjSBgAAAAAACAAAAAAAAACo0gYAAAAAAAgAAAAAAAAAwNIGAAAAAAAIAAAAAAAAANjSBgAAAAAACAAAAAAAAAD40gYAAAAAAAgAAAAAAAAAGNMGAAAAAAAIAAAAAAAAADjTBgAAAAAACAAAAAAAAABY0wYAAAAAAAgAAAAAAAAAeNMGAAAAAAAIAAAAAAAAAJjTBgAAAAAACAAAAAAAAAC40wYAAAAAAAgAAAAAAAAA2NMGAAAAAAAIAAAAAAAAAPjTBgAAAAAACAAAAAAAAAAQ1AYAAAAAAAgAAAAAAAAAMNQGAAAAAAAIAAAAAAAAAFDUBgAAAAAACAAAAAAAAABw1AYAAAAAAAgAAAAAAAAAkNQGAAAAAAAIAAAAAAAAALDUBgAAAAAACAAAAAAAAADQ1AYAAAAAAAgAAAAAAAAA8NQG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AAAAAADwYAgAAAAAAAgAAAAAAAAAEGEIAAAAAAAIAAAAAAAAADBhCAAAAAAACAAAAAAAAABIYQgAAAAAAAgAAAAAAAAAaGEIAAAAAAAIAAAAAAAAAIhhCAAAAAAACAAAAAAAAACoYQgAAAAAAAgAAAAAAAAAwGEIAAAAAAAIAAAAAAAAAOBhCAAAAAAACAAAAAAAAAAAYggAAAAAAAgAAAAAAAAAIGIIAAAAAAAIAAAAAAAAAEBiCAAAAAAACAAAAAAAAABgYggAAAAAAAgAAAAAAAAAgGIIAAAAAAAIAAAAAAAAAKBiCAAAAAAACAAAAAAAAADAYggAAAAAAAgAAAAAAAAA4GIIAAAAAAAIAAAAAAAAAABkCAAAAAAACAAAAAAAAAAoZAgAAAAAAAgAAAAAAAAAQGQIAAAAAAAIAAAAAAAAAJBkCAAAAAAACAAAAAAAAACoZAgAAAAAAAgAAAAAAAAAGGUIAAAAAAAIAAAAAAAAAFBlCAAAAAAACAAAAAAAAACgZQgAAAAAAAgAAAAAAAAA2GUIAAAAAAAIAAAAAAAAAPBlCAAAAAAACAAAAAAAAAAQZggAAAAAAAgAAAAAAAAAMGYIAAAAAAAIAAAAAAAAAKBmCAAAAAAACAAAAAAAAADYZggAAAAAAAgAAAAAAAAA8GYIAAAAAAAIAAAAAAAAADBnCAAAAAAACAAAAAAAAABIZwgAAAAAAAgAAAAAAAAAIGgIAAAAAAAIAAAAAAAAADhoCAAAAAAACAAAAAAAAABAaQgAAAAAAAgAAAAAAAAAcGkIAAAAAAAIAAAAAAAAAAhrCAAAAAAACAAAAAAAAABYawgAAAAAAAgAAAAAAAAAiGsIAAAAAAAIAAAAAAAAAGhsCAAAAAAACAAAAAAAAACAbAgAAAAAAAgAAAAAAAAAwGwIAAAAAAAIAAAAAAAAAPhsCAAAAAAACAAAAAAAAAAobQgAAAAAAAgAAAAAAAAAYG0IAAAAAAAIAAAAAAAAAJBtCAAAAAAACAAAAAAAAADAbQgAAAAAAAgAAAAAAAAA8G0IAAAAAAAIAAAAAAAAACBuCAAAAAAACAAAAAAAAABQbggAAAAAAAgAAAAAAAAAgG4IAAAAAAAIAAAAAAAAALBuCAAAAAAACAAAAAAAAADgbggAAAAAAAgAAAAAAAAAEG8IAAAAAAAIAAAAAAAAAEBvCAAAAAAACAAAAAAAAABwbwgAAAAAAAgAAAAAAAAAkG8IAAAAAAAIAAAAAAAAALBvCAAAAAAACAAAAAAAAADIbwgAAAAAAAgAAAAAAAAA6G8IAAAAAAAIAAAAAAAAAAhwCAAAAAAACAAAAAAAAAAocAgAAAAAAAgAAAAAAAAASHAIAAAAAAAIAAAAAAAAAGhwCAAAAAAACAAAAAAAAACIcAgAAAAAAAgAAAAAAAAAqHAIAAAAAAAIAAAAAAAAAMhwCAAAAAAACAAAAAAAAADgcAgAAAAAAAgAAAAAAAAAAHEIAAAAAAAIAAAAAAAAACBxCAAAAAAACAAAAAAAAABAcQgAAAAAAAgAAAAAAAAAYHEIAAAAAAAIAAAAAAAAAIBxCAAAAAAACAAAAAAAAACgcQgAAAAAAAgAAAAAAAAAwHEIAAAAAAAIAAAAAAAAAOBxCAAAAAAACAAAAAAAAAAAcggAAAAAAAgAAAAAAAAAIHIIAAAAAAAIAAAAAAAAAEByCAAAAAAACAAAAAAAAABgcggAAAAAAAgAAAAAAAAA2HIIAAAAAAAIAAAAAAAAADh0CAAAAAAACAAAAAAAAAD4dAgAAAAAAAgAAAAAAAAAOHoIAAAAAAAIAAAAAAAAAFB6CAAAAAAACAAAAAAAAACQeggAAAAAAAgAAAAAAAAAqH0IAAAAAAAIAAAAAAAAADB+CAAAAAAACAAAAAAAAABIfggAAAAAAAgAAAAAAAAAWH4IAAAAAAAIAAAAAAAAADCCCAAAAAAACAAAAAAAAABIgggAAAAAAAgAAAAAAAAAYIIIAAAAAAAIAAAAAAAAALiCCAAAAAAACAAAAAAAAADQgggAAAAAAAgAAAAAAAAAUIMIAAAAAAAIAAAAAAAAAIiDCAAAAAAACAAAAAAAAACAhQgAAAAAAAgAAAAAAAAAwIUIAAAAAAAIAAAAAAAAAACGCAAAAAAACAAAAAAAAABAhggAAAAAAAgAAAAAAAAAeIYIAAAAAAAIAAAAAAAAALCGCAAAAAAACAAAAAAAAACgjwgAAAAAAAgAAAAAAAAAuI8IAAAAAAAIAAAAAAAAANiPCAAAAAAACAAAAAAAAADwjwgAAAAAAAgAAAAAAAAAEJAIAAAAAAAIAAAAAAAAACiQCAAAAAAACAAAAAAAAABIkAgAAAAAAAgAAAAAAAAAYJAIAAAAAAAIAAAAAAAAAICQCAAAAAAACAAAAAAAAACYkAgAAAAAAAgAAAAAAAAAuJAIAAAAAAAIAAAAAAAAANCQCAAAAAAACAAAAAAAAABIkwgAAAAAAAgAAAAAAAAA+KUIAAAAAAAIAAAAAAAAABCmCAAAAAAACAAAAAAAAABQpwgAAAAAAAgAAAAAAAAA2KcIAAAAAAAIAAAAAAAAAJipCAAAAAAACAAAAAAAAADIqQgAAAAAAAgAAAAAAAAA+KkIAAAAAAAIAAAAAAAAACiqCAAAAAAACAAAAAAAAABYqggAAAAAAAgAAAAAAAAAiKoIAAAAAAAIAAAAAAAAAACwCAAAAAAACAAAAAAAAABIsAgAAAAAAAgAAAAAAAAAKMQIAAAAAAAIAAAAAAAAAADGCAAAAAAACAAAAAAAAABgxggAAAAAAAgAAAAAAAAAoMYIAAAAAAAIAAAAAAAAAMjGCAAAAAAACAAAAAAAAAD4xggAAAAAAAgAAAAAAAAAGMcIAAAAAAAIAAAAAAAAADDHCAAAAAAACAAAAAAAAABQxwgAAAAAAAgAAAAAAAAAaMcIAAAAAAAIAAAAAAAAAIjHCAAAAAAACAAAAAAAAACgxwgAAAAAAAgAAAAAAAAAwMcIAAAAAAAIAAAAAAAAANjHCAAAAAAACAAAAAAAAAD4xwgAAAAAAAgAAAAAAAAAEMgIAAAAAAAIAAAAAAAAADDICAAAAAAACAAAAAAAAABQyAgAAAAAAAgAAAAAAAAAaMgIAAAAAAAIAAAAAAAAAJDICAAAAAAACAAAAAAAAADAyAgAAAAAAAgAAAAAAAAAEM0IAAAAAAAIAAAAAAAAAHjNCAAAAAAACAAAAAAAAAA42QgAAAAAAAgAAAAAAAAAIN4IAAAAAAAIAAAAAAAAAEDeCAAAAAAACAAAAAAAAABg3ggAAAAAAAgAAAAAAAAAeN4IAAAAAAAIAAAAAAAAAJjeCAAAAAAACAAAAAAAAACw3ggAAAAAAAgAAAAAAAAA0N4IAAAAAAAIAAAAAAAAAOjeCAAAAAAACAAAAAAAAAAI3wgAAAAAAAgAAAAAAAAAIN8IAAAAAAAIAAAAAAAAAEDfCAAAAAAACAAAAAAAAABY3wgAAAAAAAgAAAAAAAAAeN8IAAAAAAAIAAAAAAAAAJDfCAAAAAAACAAAAAAAAACw3wgAAAAA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AAgAAAAAAAAAeBwKAAAAAAAIAAAAAAAAAIAcCgAAAAAACAAAAAAAAACYHAoAAAAAAAgAAAAAAAAAoBwKAAAAAAAIAAAAAAAAAKgcCgAAAAAACAAAAAAAAACwHAoAAAAAAAgAAAAAAAAAuBwKAAAAAAAIAAAAAAAAAMAcCgAAAAAACAAAAAAAAADIHAoAAAAAAAgAAAAAAAAA0BwKAAAAAAAIAAAAAAAAANgcCgAAAAAACAAAAAAAAADwHAoAAAAAAAgAAAAAAAAACB0KAAAAAAAIAAAAAAAAACAdCgAAAAAACAAAAAAAAAA4HQoAAAAAAAgAAAAAAAAAUB0KAAAAAAAIAAAAAAAAAFgdCgAAAAAACAAAAAAAAABgHQoAAAAAAAgAAAAAAAAAaB0KAAAAAAAIAAAAAAAAAIAdCgAAAAAACAAAAAAAAACIHQoAAAAAAAgAAAAAAAAAoB0KAAAAAAAIAAAAAAAAALgdCgAAAAAACAAAAAAAAADAHQoAAAAAAAgAAAAAAAAAyB0KAAAAAAAIAAAAAAAAANAdCgAAAAAACAAAAAAAAADoHQoAAAAAAAgAAAAAAAAA8B0KAAAAAAAIAAAAAAAAAAgeCgAAAAAACAAAAAAAAAAQHgoAAAAAAAgAAAAAAAAAKB4KAAAAAAAIAAAAAAAAAEAeCgAAAAAACAAAAAAAAABQHgoAAAAAAAgAAAAAAAAAaB4KAAAAAAAIAAAAAAAAAIAeCgAAAAAACAAAAAAAAACYHgoAAAAAAAgAAAAAAAAAsB4KAAAAAAAIAAAAAAAAAMgeCgAAAAAACAAAAAAAAADgHgoAAAAAAAgAAAAAAAAA+B4KAAAAAAAIAAAAAAAAAAAfCgAAAAAACAAAAAAAAAAQHwoAAAAAAAgAAAAAAAAAIB8KAAAAAAAIAAAAAAAAADgfCgAAAAAACAAAAAAAAABAHwoAAAAAAAgAAAAAAAAAWB8KAAAAAAAIAAAAAAAAAHAfCgAAAAAACAAAAAAAAACAHwoAAAAAAAgAAAAAAAAAkB8KAAAAAAAIAAAAAAAAAKAfCgAAAAAACAAAAAAAAAC4HwoAAAAAAAgAAAAAAAAA0B8KAAAAAAAIAAAAAAAAAOgfCgAAAAAACAAAAAAAAAAAIAoAAAAAAAgAAAAAAAAACCAKAAAAAAAIAAAAAAAAACAgCgAAAAAACAAAAAAAAAA4IAoAAAAAAAgAAAAAAAAAQCAKAAAAAAAIAAAAAAAAAFggCgAAAAAACAAAAAAAAABwIAoAAAAAAAgAAAAAAAAAiCAKAAAAAAAIAAAAAAAAAKAgCgAAAAAACAAAAAAAAAC4IAoAAAAAAAgAAAAAAAAA0CAKAAAAAAAIAAAAAAAAAOggCgAAAAAACAAAAAAAAAAAIQoAAAAAAAgAAAAAAAAAGCEKAAAAAAAIAAAAAAAAADAhCgAAAAAACAAAAAAAAABAIQoAAAAAAAgAAAAAAAAAUCEKAAAAAAAIAAAAAAAAAGAhCgAAAAAACAAAAAAAAABwIQoAAAAAAAgAAAAAAAAAgCEKAAAAAAAIAAAAAAAAAJAhCgAAAAAACAAAAAAAAACgIQoAAAAAAAgAAAAAAAAAsCEKAAAAAAAIAAAAAAAAAMAhCgAAAAAACAAAAAAAAADQIQoAAAAAAAgAAAAAAAAA4CEKAAAAAAAIAAAAAAAAAPAhCgAAAAAACAAAAAAAAAAAIgoAAAAAAAgAAAAAAAAAECIKAAAAAAAIAAAAAAAAACAiCgAAAAAACAAAAAAAAAAwIgoAAAAAAAgAAAAAAAAAQCIKAAAAAAAIAAAAAAAAAFAiCgAAAAAACAAAAAAAAABgIgoAAAAAAAgAAAAAAAAAcCIKAAAAAAAIAAAAAAAAAIAiCgAAAAAACAAAAAAAAACQIgoAAAAAAAgAAAAAAAAAoCIKAAAAAAAIAAAAAAAAALAiCgAAAAAACAAAAAAAAADAIgoAAAAAAAgAAAAAAAAA0CIKAAAAAAAIAAAAAAAAAOAiCgAAAAAACAAAAAAAAADwIgoAAAAAAAgAAAAAAAAAACMKAAAAAAAIAAAAAAAAABgjCgAAAAAACAAAAAAAAAAwIwoAAAAAAAgAAAAAAAAAQCMKAAAAAAAIAAAAAAAAAFgjCgAAAAAACAAAAAAAAABwIwoAAAAAAAgAAAAAAAAAgCMKAAAAAAAIAAAAAAAAAJgjCgAAAAAACAAAAAAAAACgIwoAAAAAAAgAAAAAAAAAuCMKAAAAAAAIAAAAAAAAAMAjCgAAAAAACAAAAAAAAADIIwoAAAAAAAgAAAAAAAAA0CMKAAAAAAAIAAAAAAAAANgjCgAAAAAACAAAAAAAAADgIwoAAAAAAAgAAAAAAAAA6CMKAAAAAAAIAAAAAAAAAPAjCgAAAAAACAAAAAAAAAD4IwoAAAAAAAgAAAAAAAAAECQKAAAAAAAIAAAAAAAAACgkCgAAAAAACAAAAAAAAAAwJAoAAAAAAAgAAAAAAAAAOCQKAAAAAAAIAAAAAAAAAEAkCgAAAAAACAAAAAAAAABYJAoAAAAAAAgAAAAAAAAAYCQKAAAAAAAIAAAAAAAAAHgkCgAAAAAACAAAAAAAAACQJAoAAAAAAAgAAAAAAAAAmCQKAAAAAAAIAAAAAAAAAKAkCgAAAAAACAAAAAAAAACoJAoAAAAAAAgAAAAAAAAAuCQKAAAAAAAIAAAAAAAAANAkCgAAAAAACAAAAAAAAADoJAoAAAAAAAgAAAAAAAAAACUKAAAAAAAIAAAAAAAAABglCgAAAAAACAAAAAAAAAAwJQoAAAAAAAgAAAAAAAAAQCUKAAAAAAAIAAAAAAAAAFglCgAAAAAACAAAAAAAAABwJQoAAAAAAAgAAAAAAAAAiCUKAAAAAAAIAAAAAAAAAKAlCgAAAAAACAAAAAAAAACoJQoAAAAAAAgAAAAAAAAAwCUKAAAAAAAIAAAAAAAAANglCgAAAAAACAAAAAAAAADgJQoAAAAAAAgAAAAAAAAA+CUKAAAAAAAIAAAAAAAAABAmCgAAAAAACAAAAAAAAAAoJgoAAAAAAAgAAAAAAAAAMCYKAAAAAAAIAAAAAAAAADgmCgAAAAAACAAAAAAAAABAJgoAAAAAAAgAAAAAAAAAWCYKAAAAAAAIAAAAAAAAAGAmCgAAAAAACAAAAAAAAAB4JgoAAAAAAAgAAAAAAAAAkCYKAAAAAAAIAAAAAAAAAJgmCgAAAAAACAAAAAAAAACgJgoAAAAAAAgAAAAAAAAAqCYKAAAAAAAIAAAAAAAAALgmCgAAAAAACAAAAAAAAADIJgoAAAAAAAgAAAAAAAAA2CYKAAAAAAAIAAAAAAAAAOgmCgAAAAAACAAAAAAAAAD4JgoAAAAAAAgAAAAAAAAACCcKAAAAAAAIAAAAAAAAABgnCgAAAAAACAAAAAAAAAAwJwoAAAAAAAgAAAAAAAAAOCcKAAAAAAAIAAAAAAAAAFAnCgAAAAAACAAAAAAAAABYJwoAAAAAAAgAAAAAAAAAcCcKAAAAAAAIAAAAAAAAAHgnCgAAAAAACAAAAAAAAACAJwoAAAAAAAgAAAAAAAAAiCcKAAAAAAAIAAAAAAAAAJAnCgAAAAAACAAAAAAAAACYJwoAAAAAAAgAAAAAAAAAoCcKAAAAAAAIAAAAAAAAAKgnCgAAAAAACAAAAAAAAACwJwoAAAAAAAgAAAAAAAAAyCcKAAAAAAAIAAAAAAAAAOAnCgAAAAAACAAAAAAAAADoJwoAAAAAAAgAAAAAAAAA8CcKAAAAAAAIAAAAAAAAAPgnCgAAAAAACAAAAAAAAAAQKAoAAAAAAAgAAAAAAAAAGCgKAAAAAAAIAAAAAAAAADAoCgAAAAAACAAAAAAAAABIKAoAAAAAAAgAAAAAAAAAUCgKAAAAAAAIAAAAAAAAAFgoCgAAAAAACAAAAAAAAABgKAoAAAAAAAgAAAAAAAAAcCgKAAAAAAAIAAAAAAAAAIgoCgAAAAAACAAAAAAAAACQKAoAAAAAAAgAAAAAAAAAqCgKAAAAAAAIAAAAAAAAAMAoCgAAAAAACAAAAAAAAADYKAoAAAAAAAgAAAAAAAAA8CgKAAAAAAAIAAAAAAAAAAgpCgAAAAAACAAAAAAAAAAgKQoAAAAAAAgAAAAAAAAAOCkKAAAAAAAIAAAAAAAAAFApCgAAAAAACAAAAAAAAABoKQoAAAAAAAgAAAAAAAAAgCkKAAAAAAAIAAAAAAAAAJgpCgAAAAAACAAAAAAAAACwKQoAAAAAAAgAAAAAAAAAwCkKAAAAAAAIAAAAAAAAANApCgAAAAAACAAAAAAAAADgKQoAAAAAAAgAAAAAAAAA8CkKAAAAAAAIAAAAAAAAAAAqCgAAAAAACAAAAAAAAAAQKgoAAAAAAAgAAAAAAAAAICoKAAAAAAAIAAAAAAAAADAqCgAAAAAACAAAAAAAAABAKgoAAAAAAAgAAAAAAAAAUCoKAAAAAAAIAAAAAAAAAGAqCgAAAAAACAAAAAAAAABwKgoAAAAAAAgAAAAAAAAAgCoKAAAAAAAIAAAAAAAAAJAqCgAAAAAACAAAAAAAAACgKgoAAAAAAAgAAAAAAAAAsCoKAAAAAAAIAAAAAAAAAMAqCgAAAAAACAAAAAAAAADQKgoAAAAAAAgAAAAAAAAA4CoKAAAAAAAIAAAAAAAAAPAqCgAAAAAACAAAAAAAAAAAKwoAAAAAAAgAAAAAAAAAECsKAAAAAAAIAAAAAAAAACArCgAAAAAACAAAAAAAAAAwKwoAAAAAAAgAAAAAAAAAQCsKAAAAAAAIAAAAAAAAAFArCgAAAAAACAAAAAAAAABgKwoAAAAAAAgAAAAAAAAAcCsKAAAAAAAIAAAAAAAAAIArCgAAAAAACAAAAAAAAACQKwoAAAAAAAgAAAAAAAAAoCsKAAAAAAAIAAAAAAAAALArCgAAAAAACAAAAAAAAADAKwoAAAAAAAgAAAAAAAAA0CsKAAAAAAAIAAAAAAAAAOArCgAAAAAACAAAAAAAAADwKwoAAAAAAAgAAAAAAAAAACwKAAAAAAAIAAAAAAAAABAsCgAAAAAACAAAAAAAAAAgLAoAAAAAAAgAAAAAAAAAMCwKAAAAAAAIAAAAAAAAAEAsCgAAAAAACAAAAAAAAABQLAoAAAAAAAgAAAAAAAAAYCwKAAAAAAAIAAAAAAAAAHAsCgAAAAAACAAAAAAAAACALAoAAAAAAAgAAAAAAAAAkCwKAAAAAAAIAAAAAAAAAKAsCgAAAAAACAAAAAAAAACwLAoAAAAAAAgAAAAAAAAAwCwKAAAAAAAIAAAAAAAAANAsCgAAAAAACAAAAAAAAADgLAoAAAAAAAgAAAAAAAAA8CwKAAAAAAAIAAAAAAAAAAAtCgAAAAAACAAAAAAAAAAQLQoAAAAAAAgAAAAAAAAAIC0KAAAAAAAIAAAAAAAAADAtCgAAAAAACAAAAAAAAABALQoAAAAAAAgAAAAAAAAAWC0KAAAAAAAIAAAAAAAAAHAtCgAAAAAACAAAAAAAAACILQoAAAAAAAgAAAAAAAAAmC0KAAAAAAAIAAAAAAAAALAtCgAAAAAACAAAAAAAAADILQoAAAAAAAgAAAAAAAAA4C0KAAAAAAAIAAAAAAAAAPgtCgAAAAAACAAAAAAAAAAQLgoAAAAAAAgAAAAAAAAAKC4KAAAAAAAIAAAAAAAAAEAuCgAAAAAACAAAAAAAAABYLgoAAAAAAAgAAAAAAAAAaC4KAAAAAAAIAAAAAAAAAHguCgAAAAAACAAAAAAAAACILgoAAAAAAAgAAAAAAAAAmC4KAAAAAAAIAAAAAAAAALAuCgAAAAAACAAAAAAAAADILgoAAAAAAAgAAAAAAAAA4C4KAAAAAAAIAAAAAAAAAPguCgAAAAAACAAAAAAAAAAQLwoAAAAAAAgAAAAAAAAAGC8KAAAAAAAIAAAAAAAAADAvCgAAAAAACAAAAAAAAAA4LwoAAAAAAAgAAAAAAAAAQC8KAAAAAAAIAAAAAAAAAEgvCgAAAAAACAAAAAAAAABQLwoAAAAAAAgAAAAAAAAAWC8KAAAAAAAIAAAAAAAAAGAvCgAAAAAACAAAAAAAAABoLwoAAAAAAAgAAAAAAAAAcC8KAAAAAAAIAAAAAAAAAIAvCgAAAAAACAAAAAAAAACYLwoAAAAAAAgAAAAAAAAAoC8KAAAAAAAIAAAAAAAAALgvCgAAAAAACAAAAAAAAADALwoAAAAAAAgAAAAAAAAAyC8KAAAAAAAIAAAAAAAAANAvCgAAAAAACAAAAAAAAADoLwoAAAAAAAgAAAAAAAAA8C8KAAAAAAAIAAAAAAAAAAgwCgAAAAAACAAAAAAAAAAgMAoAAAAAAAgAAAAAAAAAKDAKAAAAAAAIAAAAAAAAADAwCgAAAAAACAAAAAAAAAA4MAoAAAAAAAgAAAAAAAAAUDAKAAAAAAAIAAAAAAAAAFgwCgAAAAAACAAAAAAAAABwMAoAAAAAAAgAAAAAAAAAeDAKAAAAAAAIAAAAAAAAAJAwCgAAAAAACAAAAAAAAACYMAoAAAAAAAgAAAAAAAAAqDAKAAAAAAAIAAAAAAAAALgwCgAAAAAACAAAAAAAAADQMAoAAAAAAAgAAAAAAAAA2DAKAAAAAAAIAAAAAAAAAPAwCgAAAAAACAAAAAAAAAD4MAoAAAAAAAgAAAAAAAAAEDEKAAAAAAAIAAAAAAAAACgxCgAAAAAACAAAAAAAAABAMQoAAAAAAAgAAAAAAAAAWDEKAAAAAAAIAAAAAAAAAGgxCgAAAAAACAAAAAAAAAB4MQoAAAAAAAgAAAAAAAAAiDEKAAAAAAAIAAAAAAAAAJgxCgAAAAAACAAAAAAAAACoMQoAAAAAAAgAAAAAAAAAuDEKAAAAAAAIAAAAAAAAAMgxCgAAAAAACAAAAAAAAADYMQoAAAAAAAgAAAAAAAAA6DEKAAAAAAAIAAAAAAAAAPgxCgAAAAAACAAAAAAAAAAIMgoAAAAAAAgAAAAAAAAAGDIKAAAAAAAIAAAAAAAAACgyCgAAAAAACAAAAAAAAAA4MgoAAAAAAAgAAAAAAAAASDIKAAAAAAAIAAAAAAAAAFgyCgAAAAAACAAAAAAAAABoMgoAAAAAAAgAAAAAAAAAeDIKAAAAAAAIAAAAAAAAAIgyCgAAAAAACAAAAAAAAACYMgoAAAAAAAgAAAAAAAAAqDIKAAAAAAAIAAAAAAAAALgyCgAAAAAACAAAAAAAAADIMgoAAAAAAAgAAAAAAAAA2DIKAAAAAAAIAAAAAAAAAOgyCgAAAAAACAAAAAAAAAD4MgoAAAAAAAgAAAAAAAAACDMKAAAAAAAIAAAAAAAAABgzCgAAAAAACAAAAAAAAAAoMwoAAAAAAAgAAAAAAAAAODMKAAAAAAAIAAAAAAAAAEgzCgAAAAAACAAAAAAAAABYMwoAAAAAAAgAAAAAAAAAaDMKAAAAAAAIAAAAAAAAAHgzCgAAAAAACAAAAAAAAACIMwoAAAAAAAgAAAAAAAAAmDMKAAAAAAAIAAAAAAAAAKgzCgAAAAAACAAAAAAAAAC4MwoAAAAAAAgAAAAAAAAAyDMKAAAAAAAIAAAAAAAAANgzCgAAAAAACAAAAAAAAADoMwoAAAAAAAgAAAAAAAAA+DMKAAAAAAAIAAAAAAAAAAg0CgAAAAAACAAAAAAAAAAYNAoAAAAAAAgAAAAAAAAAKDQKAAAAAAAIAAAAAAAAADg0CgAAAAAACAAAAAAAAABINAoAAAAAAAgAAAAAAAAAWDQKAAAAAAAIAAAAAAAAAGg0CgAAAAAACAAAAAAAAAB4NAoAAAAAAAgAAAAAAAAAiDQKAAAAAAAIAAAAAAAAAJg0CgAAAAAACAAAAAAAAACoNAoAAAAAAAgAAAAAAAAAuDQKAAAAAAAIAAAAAAAAAMg0CgAAAAAACAAAAAAAAADYNAoAAAAAAAgAAAAAAAAA6DQKAAAAAAAIAAAAAAAAAPg0CgAAAAAACAAAAAAAAAAINQoAAAAAAAgAAAAAAAAAGDUKAAAAAAAIAAAAAAAAACg1CgAAAAAACAAAAAAAAAA4NQoAAAAAAAgAAAAAAAAASDUKAAAAAAAIAAAAAAAAAFg1CgAAAAAACAAAAAAAAABoNQoAAAAAAAgAAAAAAAAAeDUKAAAAAAAIAAAAAAAAAIg1CgAAAAAACAAAAAAAAACYNQoAAAAAAAgAAAAAAAAAqDUKAAAAAAAIAAAAAAAAALg1CgAAAAAACAAAAAAAAADINQoAAAAAAAgAAAAAAAAA2DUKAAAAAAAIAAAAAAAAAOg1CgAAAAAACAAAAAAAAAD4NQoAAAAAAAgAAAAAAAAACDYKAAAAAAAIAAAAAAAAABg2CgAAAAAACAAAAAAAAAAoNgoAAAAAAAgAAAAAAAAAODYKAAAAAAAIAAAAAAAAAEg2CgAAAAAACAAAAAAAAABYNgoAAAAAAAgAAAAAAAAAaDYKAAAAAAAIAAAAAAAAAHg2CgAAAAAACAAAAAAAAACINgoAAAAAAAgAAAAAAAAAmDYKAAAAAAAIAAAAAAAAAKg2CgAAAAAACAAAAAAAAAC4NgoAAAAAAAgAAAAAAAAAyDYKAAAAAAAIAAAAAAAAANg2CgAAAAAACAAAAAAAAADoNgoAAAAAAAgAAAAAAAAA+DYKAAAAAAAIAAAAAAAAAAg3CgAAAAAACAAAAAAAAAAYNwoAAAAAAAgAAAAAAAAAMDcKAAAAAAAIAAAAAAAAADg3CgAAAAAACAAAAAAAAABQNwoAAAAAAAgAAAAAAAAAWDcKAAAAAAAIAAAAAAAAAHA3CgAAAAAACAAAAAAAAAB4NwoAAAAAAAgAAAAAAAAAkDcKAAAAAAAIAAAAAAAAAJg3CgAAAAAACAAAAAAAAACwNwoAAAAAAAgAAAAAAAAAuDcKAAAAAAAIAAAAAAAAANA3CgAAAAAACAAAAAAAAADYNwoAAAAAAAgAAAAAAAAA8DcKAAAAAAAIAAAAAAAAAPg3CgAAAAAACAAAAAAAAAAQOAoAAAAAAAgAAAAAAAAAGDgKAAAAAAAIAAAAAAAAADA4CgAAAAAACAAAAAAAAAA4OAoAAAAAAAgAAAAAAAAAUDgKAAAAAAAIAAAAAAAAAFg4CgAAAAAACAAAAAAAAABwOAoAAAAAAAgAAAAAAAAAiDgKAAAAAAAIAAAAAAAAAKA4CgAAAAAACAAAAAAAAAC4OAoAAAAAAAgAAAAAAAAA0DgKAAAAAAAIAAAAAAAAAOg4CgAAAAAACAAAAAAAAADwOAoAAAAAAAgAAAAAAAAACDkKAAAAAAAIAAAAAAAAACA5CgAAAAAACAAAAAAAAAAoOQoAAAAAAAgAAAAAAAAAODkKAAAAAAAIAAAAAAAAAFA5CgAAAAAACAAAAAAAAABYOQoAAAAAAAgAAAAAAAAAcDkKAAAAAAAIAAAAAAAAAIg5CgAAAAAACAAAAAAAAACgOQoAAAAAAAgAAAAAAAAAuDkKAAAAAAAIAAAAAAAAANA5CgAAAAAACAAAAAAAAADoOQoAAAAAAAgAAAAAAAAAADoKAAAAAAAIAAAAAAAAABg6CgAAAAAACAAAAAAAAAAwOgoAAAAAAAgAAAAAAAAASDoKAAAAAAAIAAAAAAAAAGA6CgAAAAAACAAAAAAAAAB4OgoAAAAAAAgAAAAAAAAAkDoKAAAAAAAIAAAAAAAAAKg6CgAAAAAACAAAAAAAAADAOgoAAAAAAAgAAAAAAAAA2DoKAAAAAAAIAAAAAAAAAPA6CgAAAAAACAAAAAAAAAAIOwoAAAAAAAgAAAAAAAAAIDsKAAAAAAAIAAAAAAAAADg7CgAAAAAACAAAAAAAAABQOwoAAAAAAAgAAAAAAAAAaDsKAAAAAAAIAAAAAAAAAIA7CgAAAAAACAAAAAAAAACIOwoAAAAAAAgAAAAAAAAAoDsKAAAAAAAIAAAAAAAAALA7CgAAAAAACAAAAAAAAADIOwoAAAAAAAgAAAAAAAAA2DsKAAAAAAAIAAAAAAAAAPA7CgAAAAAACAAAAAAAAAAIPAoAAAAAAAgAAAAAAAAAIDwKAAAAAAAIAAAAAAAAADg8CgAAAAAACAAAAAAAAABAPAoAAAAAAAgAAAAAAAAAWDwKAAAAAAAIAAAAAAAAAGA8CgAAAAAACAAAAAAAAABwPAoAAAAAAAgAAAAAAAAAgDwKAAAAAAAIAAAAAAAAAJA8CgAAAAAACAAAAAAAAACgPAoAAAAAAAgAAAAAAAAAsDwKAAAAAAAIAAAAAAAAAMA8CgAAAAAACAAAAAAAAADQPAoAAAAAAAgAAAAAAAAA4DwKAAAAAAAIAAAAAAAAAPA8CgAAAAAACAAAAAAAAAAAPQoAAAAAAAgAAAAAAAAAED0KAAAAAAAIAAAAAAAAACA9CgAAAAAACAAAAAAAAAAwPQoAAAAAAAgAAAAAAAAAQD0KAAAAAAAIAAAAAAAAAFA9CgAAAAAACAAAAAAAAABgPQoAAAAAAAgAAAAAAAAAcD0KAAAAAAAIAAAAAAAAAIA9CgAAAAAACAAAAAAAAACQPQoAAAAAAAgAAAAAAAAAoD0KAAAAAAAIAAAAAAAAALA9CgAAAAAACAAAAAAAAADAPQoAAAAAAAgAAAAAAAAA2D0KAAAAAAAIAAAAAAAAAOA9CgAAAAAACAAAAAAAAAD4PQoAAAAAAAgAAAAAAAAAAD4KAAAAAAAIAAAAAAAAABg+CgAAAAAACAAAAAAAAAAgPgoAAAAAAAgAAAAAAAAAOD4KAAAAAAAIAAAAAAAAAEA+CgAAAAAACAAAAAAAAABYPgoAAAAAAAgAAAAAAAAAYD4KAAAAAAAIAAAAAAAAAHA+CgAAAAAACAAAAAAAAACAPgoAAAAAAAgAAAAAAAAAkD4KAAAAAAAIAAAAAAAAAKA+CgAAAAAACAAAAAAAAACwPgoAAAAAAAgAAAAAAAAAwD4KAAAAAAAIAAAAAAAAANA+CgAAAAAACAAAAAAAAADgPgoAAAAAAAgAAAAAAAAA8D4KAAAAAAAIAAAAAAAAAAA/CgAAAAAACAAAAAAAAAAQPwoAAAAAAAgAAAAAAAAAID8KAAAAAAAIAAAAAAAAADA/CgAAAAAACAAAAAAAAABAPwoAAAAAAAgAAAAAAAAAUD8KAAAAAAAIAAAAAAAAAGA/CgAAAAAACAAAAAAAAABwPwoAAAAAAAgAAAAAAAAAgD8KAAAAAAAIAAAAAAAAAJA/CgAAAAAACAAAAAAAAACgPwoAAAAAAAgAAAAAAAAAuD8KAAAAAAAIAAAAAAAAAMA/CgAAAAAACAAAAAAAAADYPwoAAAAAAAgAAAAAAAAA4D8KAAAAAAAIAAAAAAAAAOg/CgAAAAAACAAAAAAAAADwPwoAAAAAAAgAAAAAAAAA+D8KAAAAAAAIAAAAAAAAAABACgAAAAAACAAAAAAAAAAIQAoAAAAAAAgAAAAAAAAAEEAKAAAAAAAIAAAAAAAAABhACgAAAAAACAAAAAAAAAAoQAoAAAAAAAgAAAAAAAAAQEAKAAAAAAAIAAAAAAAAAEhACgAAAAAACAAAAAAAAABgQAoAAAAAAAgAAAAAAAAAaEAKAAAAAAAIAAAAAAAAAIBACgAAAAAACAAAAAAAAACIQAoAAAAAAAgAAAAAAAAAoEAKAAAAAAAIAAAAAAAAAKhACgAAAAAACAAAAAAAAAC4QAoAAAAAAAgAAAAAAAAAyEAKAAAAAAAIAAAAAAAAANhACgAAAAAACAAAAAAAAADwQAoAAAAAAAgAAAAAAAAA+EAKAAAAAAAIAAAAAAAAABBBCgAAAAAACAAAAAAAAAAYQQoAAAAAAAgAAAAAAAAAMEEKAAAAAAAIAAAAAAAAADhBCgAAAAAACAAAAAAAAABAQQoAAAAAAAgAAAAAAAAASEEKAAAAAAAIAAAAAAAAAGBBCgAAAAAACAAAAAAAAABoQQoAAAAAAAgAAAAAAAAAeEEKAAAAAAAIAAAAAAAAAJBBCgAAAAAACAAAAAAAAACoQQoAAAAAAAgAAAAAAAAAsEEKAAAAAAAIAAAAAAAAAMhBCgAAAAAACAAAAAAAAADgQQoAAAAAAAgAAAAAAAAA6EEKAAAAAAAIAAAAAAAAAABCCgAAAAAACAAAAAAAAAAIQgoAAAAAAAgAAAAAAAAAIEIKAAAAAAAIAAAAAAAAADhCCgAAAAAACAAAAAAAAABQQgoAAAAAAAgAAAAAAAAAaEIKAAAAAAAIAAAAAAAAAIBCCgAAAAAACAAAAAAAAACYQgoAAAAAAAgAAAAAAAAAsEIKAAAAAAAIAAAAAAAAAMhCCgAAAAAACAAAAAAAAADgQgoAAAAAAAgAAAAAAAAA+EIKAAAAAAAIAAAAAAAAABBDCgAAAAAACAAAAAAAAAAoQwoAAAAAAAgAAAAAAAAAQEMKAAAAAAAIAAAAAAAAAFhDCgAAAAAACAAAAAAAAABwQwoAAAAAAAgAAAAAAAAAiEMKAAAAAAAIAAAAAAAAAKBDCgAAAAAACAAAAAAAAAC4QwoAAAAAAAgAAAAAAAAA0EMKAAAAAAAIAAAAAAAAAOhDCgAAAAAACAAAAAAAAAAARAoAAAAAAAgAAAAAAAAAGEQKAAAAAAAIAAAAAAAAADBECgAAAAAACAAAAAAAAABIRAoAAAAAAAgAAAAAAAAAYEQKAAAAAAAIAAAAAAAAAHhECgAAAAAACAAAAAAAAACQRAoAAAAAAAgAAAAAAAAAqEQKAAAAAAAIAAAAAAAAAMBECgAAAAAACAAAAAAAAADYRAoAAAAAAAgAAAAAAAAA8EQKAAAAAAAIAAAAAAAAAAhFCgAAAAAACAAAAAAAAAAgRQoAAAAAAAgAAAAAAAAAOEUKAAAAAAAIAAAAAAAAAFBFCgAAAAAACAAAAAAAAABoRQoAAAAAAAgAAAAAAAAAgEUKAAAAAAAIAAAAAAAAAJhFCgAAAAAACAAAAAAAAACwRQoAAAAAAAgAAAAAAAAAyEUKAAAAAAAIAAAAAAAAAOBFCgAAAAAACAAAAAAAAAD4RQoAAAAAAAgAAAAAAAAAEEYKAAAAAAAIAAAAAAAAAChGCgAAAAAACAAAAAAAAABARgoAAAAAAAgAAAAAAAAAWEYKAAAAAAAIAAAAAAAAAHBGCgAAAAAACAAAAAAAAACIRgoAAAAAAAgAAAAAAAAAmEYKAAAAAAAIAAAAAAAAAKhGCgAAAAAACAAAAAAAAAC4RgoAAAAAAAgAAAAAAAAAyEYKAAAAAAAIAAAAAAAAANhGCgAAAAAACAAAAAAAAADoRgoAAAAAAAgAAAAAAAAA+EYKAAAAAAAIAAAAAAAAAAhHCgAAAAAACAAAAAAAAAAgRwoAAAAAAAgAAAAAAAAAKEcKAAAAAAAIAAAAAAAAADhHCgAAAAAACAAAAAAAAABQRwoAAAAAAAgAAAAAAAAAWEcKAAAAAAAIAAAAAAAAAHBHCgAAAAAACAAAAAAAAAB4RwoAAAAAAAgAAAAAAAAAiEcKAAAAAAAIAAAAAAAAAJhHCgAAAAAACAAAAAAAAACoRwoAAAAAAAgAAAAAAAAAuEcKAAAAAAAIAAAAAAAAAMhHCgAAAAAACAAAAAAAAADYRwoAAAAAAAgAAAAAAAAA6EcKAAAAAAAIAAAAAAAAAPhHCgAAAAAACAAAAAAAAAAISAoAAAAAAAgAAAAAAAAAGEgKAAAAAAAIAAAAAAAAADBICgAAAAAACAAAAAAAAAA4SAoAAAAAAAgAAAAAAAAAQEgKAAAAAAAIAAAAAAAAAEhICgAAAAAACAAAAAAAAABgSAoAAAAAAAgAAAAAAAAAaEgKAAAAAAAIAAAAAAAAAIBICgAAAAAACAAAAAAAAACYSAoAAAAAAAgAAAAAAAAAoEgKAAAAAAAIAAAAAAAAAKhICgAAAAAACAAAAAAAAACwSAoAAAAAAAgAAAAAAAAAyEgKAAAAAAAIAAAAAAAAAOBICgAAAAAACAAAAAAAAAD4SAoAAAAAAAgAAAAAAAAAEEkKAAAAAAAIAAAAAAAAAChJCgAAAAAACAAAAAAAAAA4SQoAAAAAAAgAAAAAAAAASEkKAAAAAAAIAAAAAAAAAFhJCgAAAAAACAAAAAAAAABoSQoAAAAAAAgAAAAAAAAAeEkKAAAAAAAIAAAAAAAAAIhJCgAAAAAACAAAAAAAAACgSQoAAAAAAAgAAAAAAAAAsEkKAAAAAAAIAAAAAAAAAMBJCgAAAAAACAAAAAAAAADQSQoAAAAAAAgAAAAAAAAA4EkKAAAAAAAIAAAAAAAAAPBJCgAAAAAACAAAAAAAAAAASgoAAAAA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AAAAAACwAAAAAAAAAAYAAAAAAAAACAAAAAAAAAAQAAAAAAAAACUAAAALAAAAAgAAAAAAAADgSwoAAAAAAOBLCgAAAAAAaAEAAAAAAAAGAAAAAQAAAAgAAAAAAAAAGAAAAAAAAAAHAAAAAwAAAAIAAAAAAAAASE0KAAAAAABITQoAAAAAANkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAHAAAAAkAAAACAAAAAAAAAChOCgAAAAAAKE4KAAAAAADgwQAAAAAAAAUAAAAAAAAACAAAAAAAAAAQAAAAAAAAADYAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAgQCwAAAAAASAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "BPFLoaderUpgradeab1e11111111111111111111111", + "executable": false, + "rentEpoch": 18446744073709552000, + "space": 1451341 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json new file mode 100644 index 000000000..d08336e2b --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json @@ -0,0 +1,14 @@ +{ + "pubkey": "REzxi9nX3Eqseha5fBiaJhTC6SFJx4qJhP83U4UCrtc", + "account": { + "lamports": 1197120, + "data": [ + "aXOuIl/pivwDAAAAAAAAAAAAAAAAAAAAGTMNENnMh1Ehjq9R6IhdBYZC4Io=", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 44 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json new file mode 100644 index 000000000..c941623d0 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json @@ -0,0 +1,14 @@ +{ + "pubkey": "Hazwi3jFQtLKc2ughi7HFXPkpDeso7DQaMR9Ks4afh3j", + "account": { + "lamports": 1197120, + "data": [ + "aXOuIl/pivwAAAAAAAAAAAAAAAAAAAAAvT+oG1i6kqghNgOLJa3scGavMVU=", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 44 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json new file mode 100644 index 000000000..88e7d81b2 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json @@ -0,0 +1,14 @@ +{ + "pubkey": "BWyFzH6LsnmDAaDWbGsriQ9SiiKq1CF6pbH4Ye3kzSBV", + "account": { + "lamports": 1197120, + "data": [ + "aXOuIl/pivwAAAAAAAAAAAAAAAAAAAAA0MPaWPVTWBQrjT4GwcMMXGEU7+g=", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 44 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json new file mode 100644 index 000000000..62200a351 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json @@ -0,0 +1,14 @@ +{ + "pubkey": "Afgq3BHEfCE7d78D2XE9Bfyu2ieDqvE24xX8KDwreBms", + "account": { + "lamports": 1649520, + "data": [ + "ogTyNJPz3WCAxc+QtIgKaKCt4UH53aPJt3kR23C3Hek9+WiJv/bN7B85CA8gikw2ifivStUw7tLLV9S1TFCZ1TETnU45u+E6pl/JidtfXUJ1nzpUYFjvzc3AvzwYmActjrRd0dgFCM4AAAAA/Q==", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 109 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json new file mode 100644 index 000000000..bca4334b6 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json @@ -0,0 +1,14 @@ +{ + "pubkey": "DBD8hAwLDRQkTsu6EqviaYNGKPnsAMmQonxf7AH8ZcFY", + "account": { + "lamports": 1405920, + "data": [ + "eoVUPzmfq86Axc+QtIgKaKCt4UH53aPJt3kR23C3Hek9+WiJv/bN7IDFz5C0iApooK3hQfndo8m3eRHbcLcd6T35aIm/9s3sAP0=", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 74 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json new file mode 100644 index 000000000..cdf85245a --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json @@ -0,0 +1,14 @@ +{ + "pubkey": "FSxJ85FXVsXSr51SeWf9ciJWTcRnqKFSmBgRDeL3KyWw", + "account": { + "lamports": 2039280, + "data": [ + "xvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWG06cUFm0yAK1JhEHKELuHdBTqROz8nrg0RaRNBnQEQ50z48TPzLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json new file mode 100644 index 000000000..3c327d6a9 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json @@ -0,0 +1,14 @@ +{ + "pubkey": "72bvEFk2Usi2uYc1SnaTNhBcQPc6tiJWXr9oKk7rkd4C", + "account": { + "lamports": 1795680, + "data": [ + "n4M6qsFUgLbWqajhGAScPd+PLCmG/7q4c+v9bJKeja8GCPmRo4QduMb6evO+2606PWXzaqvJdDGxu+TC0vbg5HymAgNFL11hABCl1OgAAACbOAAAAAAAAP8oAAAAAAAAcuTt4iFhAAAAAAAAAAAAAKClHaPafQAAAAAAAAAAAAD9/w==", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 130 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json new file mode 100644 index 000000000..b98a2bbaa --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json @@ -0,0 +1,14 @@ +{ + "pubkey": "8d1jdvvMFhJfxSzPXcDGtifcGMTvUxc2EpWFstbNzcTL", + "account": { + "lamports": 1426800, + "data": [ + "EdYtsOWVxUcAAAAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60hZjy8Y6Y4jOy9QMTMaodmv8oz5ObYth/BStPTxPbusEf8=", + "base64" + ], + "owner": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 77 + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json b/solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json new file mode 100644 index 000000000..c586fa26d --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json @@ -0,0 +1,14 @@ +{ + "pubkey": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "account": { + "lamports": 14801671630, + "data": [ + "AQAAAAwaWIb+EJPfn8Q4wpb59ydbdxi2vA4VbY0zbFjwg5ltAICAaSIj9QAGAQEAAACoBjP/Bn2I36XUNXv0TibOzM8IZmiBA8a6YJ+kTBjSCA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json b/solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json new file mode 100644 index 000000000..74bbf063c --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json @@ -0,0 +1,14 @@ +{ + "pubkey": "4tKtuvtQ4TzkkrkESnRpbfSXCEZPkZe3eL5tCFUdpxtf", + "account": { + "lamports": 2039280, + "data": [ + "xvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWEMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQC0AoadfgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/solana/programs/matching-engine/tests/fixtures/lup.json b/solana/programs/matching-engine/tests/fixtures/lup.json new file mode 100644 index 000000000..1b4276513 --- /dev/null +++ b/solana/programs/matching-engine/tests/fixtures/lup.json @@ -0,0 +1,14 @@ +{ + "pubkey": "4z6pDbpKnNKiJDkzoDwWR4SDaeaDPTF1MCL8u6Lu7Rkn", + "account": { + "lamports": 6625920, + "data": [ + "AQAAAP//////////ZwAAAAAAAAAAAQwaWIb+EJPfn8Q4wpb59ydbdxi2vA4VbY0zbFjwg5ltAAAFRe+GGewO2VKzStvwOwoGh7W8TVM/hNbddYuA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqfVFxksXFEhjMlMPUrxf1ja7gibof1E49vZigAAAAAGp9UXGMd0yShWY5hpHV62i164o5tLbVxzVVshAAAAAHJDQ3mV6t58cUpJ+e822LIcsUBTOwpHm0T5sODcq4FbHu0eVxx0uu/r+QqBA1tYR7/7xa2jK7vFhiVvxRPJ/jTTtSU8tEXZLkYEjl2BoT0kyuPidN/QEq+eBjzXGnJl7h1UtVWgROY6CpYMKGKtGfo9cAxspQVJzsHCn2swzhWjgprV4q1nmfSHUucDrc+OACq5BjhLX9VWyouVLOq2KnF/o3+x53xyAJe+5QVbhccDMFHEzIKCwTfpu66xBpC5fA4KWJpBpV+9ZsUqR18tkqbT3JtHRxFMua+CWpi1RdPOj6JJeW4cIvS98Dhl14FT/fCHBvqF+xGjP9dPer8WxCq06cUFm0yAK1JhEHKELuHdBTqROz8nrg0RaRNBnQEQ5we06MQBcetUkHocfo1RGNuNGIyzUmh4EhPtaA2C4NNspl/JQ0GaWtWQBC/WfJeR/QFaz1OlTMgj7bj/gbntci6nQTuZ1lLRTGfai+GjjmjQFZ50USufXWKA1yzXnYtGdZw7LbJZvgmkN8rBDfVp1SnURDXYd4NYAkL4wrbvHlTCpl/JidtfXUJ1nzpUYFjvzc3AvzwYmActjrRd0dgFCM4G3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqcb6evO+2606PWXzaqvJdDGxu+TC0vbg5HymAgNFL11hWY8vGOmOIzsvUDEzGqHZr/KM+Tm2LYfwUrT08T27rBHWqajhGAScPd+PLCmG/7q4c+v9bJKeja8GCPmRo4QduKj9WbEVEBTPp58Fhfe3fdnFMpoofnmBBeY8i9FkblYcVaI8JrNieKvtNPeEfTCZZMEckEBqdSSJdUX3pBmGcOM=", + "base64" + ], + "owner": "AddressLookupTab1e1111111111111111111111111", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 824 + } + } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/fixtures/mainnet_cctp_message_transmitter.so b/solana/programs/matching-engine/tests/fixtures/mainnet_cctp_message_transmitter.so new file mode 100644 index 0000000000000000000000000000000000000000..b80bb05981f02c4f551a9c6d0818ada3ad7c0bae GIT binary patch literal 1228720 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
    =>3I2RzjQlxA%6GJ#t>BAiGXLj$U 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 !ELag~p_XP3oQPU0S0w@LgR^&g@!rF-1t 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 z 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

    +fUp>$ppiI_?+c z8m+)nEg-MA2JdVC%y+abM)j6~9IN23Ia>(?{~_aD{ut4T-^s;0h1O4h{$;f1k5WBv z-B9}lxgk9XSqD^l_4m5nPVP_TW6SXgz>D(0_SyTmUbKSG?~uI*nxZp*NBtt)4RFMH zj$qYWuU=r>?0&uh{Zv8QJuiO=cLM)nyECDz0-^7Jn|~kIVNY0GM(2cxE^plur3dmU z>c@DXRIaLrrkdrP80B}oKMVA^$vC1f3H}0%pYJFuIP;ARPJLn1AXE@qUEop z`?_9_eBc$GaTb-?3#f1s(|J7Az91eDsiSr(I*swUbGz`is!n=t%hLsZX96C4ux35C*WbIZyIbtROIC%WeVE%mm|2NN$u$+)zWR@pj7Or3ZJ1kq z5>|E0f;g3KwdmIZv0v4!h-LxL2e+E}HRAu#zoX=g*u99uc(c0+9og%4OU_9Dcfj-+gP&vlilvueTeVd_!*bdj7xz8$4ERBab86gl+HqYV~e_Zygqe3&_#Se*$pkz8~NOW#$R^`SO_#aD=cxy*#=K_jd*KJB)Z_ z+;Dt}UMLHV59Tu{X}2?1{);xjO}H*hiQXm|_Vh5#FpkT^I4)j$85e1v?dN(pE`P## zXtn#N3qXH2@N;t?_n)V~fq9lK!905>((BFBvFF(~q;sr!cE0dY>&Ibu$J8Dqz0kZV zTF(0D+p9zL+-&5$KjLuw;`Sfrb{T#iEqf((;0_Ga{nfw&j@z8G>9#Wy#uvnU)Wo>H zds-asfSrWFpAUa!&yae69yvC(jDJlHPiV7+@h*dh7)eDOw89*BA|hN5I1KnPKF8_9R3Vh+@fs5=DmLL?wlt6(9OeN9>*KO;Wj_h)J417K*a%OqVN%DOY zlX2_gQvHl++)MR6JzTYx zjl8Y>qNNDk)t1?r_r+M0sY|kUDrcYFVvq#XUce}eq6)+)LqTvsrr$< zj_)rLzvhabWH09Xs3(uudV)p}t~@=N!FW+`=sAEWA9n}K-`&abziriziEm$TSPt$U zmYbX5@kV~}QaapzzOQT35;Xq;fZLrpGMgb_Nv)F0+j9>pa8u7=_g>=Xk`9(@@W{qUXrR z^FXKC2NUaJx$GL@a`#~>3-PxG_;&|buM^^DBhC-0ezu+IL9&ojH^cR^%(gR!$#&{G zSOHC&@YBo>R3D7gk2q|Akq*2JS9dgz+zsP`dbeh)%)cnto3n&VGn@5oU8^roj4$fm z#CWq;b3K$Z$_?|<&4G2l@4mtx&+KZhkMkBu%62iV{LT?N7j^J`(|Ca7vJCJdOBx5D z{J+zH^5=PCs+ciX{FbRIEDw!;F)l!j%2snfbHpyH9die`U+m{G>__a1+t2eRyH(mH zxlC8He&V_mvas#~mY3Tv^QD^UL;Hx&%{+!94&yUxJ9+$NJzv+OG>$*@Y7iJ)xK46O zwVx-T+-vYY^xGAX`^|An?!wRzn%H*h~9Po-~;#Ix?MVSb*J{(*kPlok7| zdSO=bJi+nmg7KQu%k?#mN4XQ-ck_Lne^`Gl=@Ik2ZlR1@hQ}jX1`Xj-z14Ug@gW`V zKo`>|a#{xCLGl&chSP!FR z&r*lkZtiy>3)Whaas2eI19K`Z*+dwy`TN8 z^6d&A8V5wn@5-fH=vDfZk79JW!k5w)EjvVns=YBQMSoFg;aYP!)4ApWmaFyA^ zBYfVtv-xpU|98W@BtN%F=Ap#-bI|d?<@vcTp$F=dpIgrXoa*Cfe(n!vTJ&t^`J?0P z*>TA?BF^PRullo~U3CYzz3RWtQ=DI9|4wJR=ZGB-&HiS1o~Yhx-lX)P{6BxbZ*N_z zzHM){9oGLe)OY(?pVZ#1lYL}uzotsYX%q8_{iDL6{Apb7=835=`oF$_{~NN#!|i8z zE)siu>H@@u`DT>HLG7ycW5jN(20H_&J$mOnM9my`56i{ge~tr693Lz-KT^H1*V{e> z{Hva-UD7=3!cBkxF16nmRxzLMpvsH&JX(gU$uR$VsQ^AWCeZbw)qn=+R=Lj+d+(|r z5I)w;u$ha zQ0^ShXngDKn@C=w`zEgVVSnF*_uX|~YyT=QjSCj7;C@XbkGtxt`nl}&{2cZ1LDWZy zv&Tz^H*bM|((`J@r|YKf0NXp?-!UE7-#ei{wbwfSZhw1@oabE3_%yD}-p??`&8S~l zkw5Ai{O4$1it8T;U$~0%7f%l){`2JYUdFc?l!WwTJ;O?GP>xzp*9DM|x1!u6PgOo< zg1X`eYka&K0K`8>*HtoI#HSH>T^sAkg=-ek{nd~b5tQlje%Gl6*|XwNjx&=GU^8QmqiYju>*H^usqCVFhFpbhnb!%r`ukfe+Zuu2C-s1CltNBYxz6!rYM^RT~mqA$&A;Mskhw~F7kTcmeM#_NrL zHN$ELv>(NTBX1|GM?8r}!N8O7*YK)pe|w5#B0zJ};0z*F$}bADRO3!(A`;<2h`1 z*D1MqcU;=F1=@94?XnVY=(=qb8HLM>3q-$Ao=b;d9hOP_NkM<7!gY!3Yh>lxklv7-cc;9K`T|jA_&iL%q_zMG`NQ9W7>{p0hxW%w`9v5X352~0qw80o`O$KdR|MmIX-ADVtoKn5+Q_ub*^`9sQLBfUj7h3v_@KOub_&CerbTn^M z`PW^-*e5LiE4W?u3L@jUmjC9E<^R(Ye7i{V*@7oy5C7Y4KLLAq+ZV@Y4}WaoACEo! z_NxlmLz&NdJ`?wQK=qxv`5ZXnyukf$cQNZn@?0*BoKwbl6YCrCqtWRPPz6)V`3vh~ zrK9?#aZcUEqOUN|F(NQ)Iqp=us`(_2!_^>IeO5gLUo&>z_Q^&yRIJ z8a5YT{YIF+*&{Un%tp2YX5(Rk^<1OoUtWB22Q`ECY;Ud?`=`&{KAz|9&OwCY zczJo|>A(=;JCXIuh<%AThVH9>(|N4^P2(wlU56(qKF67*{Gq(Q@Sk8$W&S51Ud2o6 zMI1I<2;Xr#(c)4{w;5b--`EddE4sSl`g!Wc-t)-@P5}V z9dIB2;(jn+LKTrPox7ytp7fP6WKV9)NaZ@K!(1!2Wu{ z9xDHOKF51sMAlpFeg1z0`mjH|bYDU3y;;TgBhD4{2i1cnLxyzXZ-HAS$%0 zSgIb{t;XmXd7zP&%05JhL6Kz1SZ0H4d#`Kt3&?buE8WW6*a{R2LXLvb8?@VyYp zm(_mg{L}fR=TZv!#fDkFU(6d(h) zOXFjtbNFOmUr{ekfhwrSRRRC^;3>ZUQ~kvHcwzn4KpgO|R(XYVfN zd7hB>E^IoU06H}8z<7)LJ)7&h+OOD6e7%DDy@bed?EW5mdVKwTy+r+8`{elg`$CEO z`}mLi{>C%jC?{LaACvJ$l}azDApv^l#Y!;VZ~L)N-)Q5VDUm#W_r&=6dvA&Q+xwK? zU%vdo(88tjz?SaLFg=j+Qr2 z$L>}@43|5f3VMGINO1H$yXZ887{=0w9ZXEGBL9l`(Qav(EkZBq#R0LuO&||9h0zh< z)APadUc7hCRlXal?@#Fa3wln-j{iXosQ<5mJhB6XmiRb;@=7wSazQ$u`4!sTh4jFE zFWi93wd^eMFZ)%|>B7J4ub357-AP0^{)sqGa9sR8=Er?FP8Cu;HC*=c0H>F7Jet0b z1(=>uPtVPDT+dw2?aX;17dc;Juk%qpF7fBX)H+w+J_quRI3Fc?CZeA&3rRmO_|5qA z^NQn4KY#gbIQqE|9O(Y=WowBzU*gjNcy=2pqFvZOorOa+aSzX}oXy^*+)|aP)Ag|40A!lmEf} zD!z~TR3X3N{T|CW{l2@N9ggq+%M1E{H+`)Bl;0b0Zx?@hN1QrNb%{US!2UGlavYQ1 z-bnr~z2yQfXE#u3R@QUbQp06qoXc1hm$j8t+Hpi1#tZ$-C$0xnN?h@kBaV~Iv{C&; zSv7qb%H7jRu7K4`yJ(rseZJqiQCD#q?_VA*`qtf>lY6~EkHr>CXjX1 zO-nt=OQYqy9vpGbB7DjI8;Kuxpq)`Yn9X z3R!>MMCj$+JPud5kZ*LO2KpHz=yu-@sumDfK{m_+%_^c?v_3u4RT`7a=Ue?!kTPr&>OTiSZi zE_%=l_fZd4pAGwMvM>CXkjqbi?y==^$ry5ZZBe;=pX9=JRr4*krhx|3jn{Lz6r~At zoQ_%sXdV0=E z_Yw4biCMds@GnXdT1WStqSLuQ-G5soeoUXwVRu6DCel&275@c20e!*idMNQuqC8Eo z+0sM(n8sd~pR6-SXGQt>>_L7m?**8aK7wzV<)5dC!n!@`5zZ6c_u38oB77H=7Xc>S zFS7I2oiHxmdjAf_>+al7_}xD4H!%ZeZQ=F{t$tO$uDri#Hu3nGBn#fie7DaZVhuOL z<7gVWUp?0ooqi?vdkq!NA{H0SFL_^%#R}sr<5bP{6o1iin$Pw0oHC9JqN-cY2=mA3 zDr%toU3dYnd$@ZJkepPm+D`X3^!-F5w(&22EGM^@<>n5^ zIEZ`_WeZuaGomlCVd~$^KE&fCa?tm&PmRwd#EasLU+J*zHJ)S9_n*{W>34y=`7uq} zi{9yeGV*f}WTtdxWdXreJ(6>Yb9g#C(dlyt(W%$79}4!Dl6mCqAA&e=L~vgP%bTv} z-y!h!LFAllw44`G^nFyc$5XZEWJe71D4D6#^XVH;uW`4Z9 zLi}~K;xy-8cx3Z640?rpUAoARKasC@*Ml7dxu9tDeQ@1(Gi%xYn6<3m_V%NIs^MT55l=f92dKPjq{L>zl9~UDDD0N-2==Q=3PgSZfN(UMK{jt^Px%= zn89AFAADV6zXc=SUt97r+neYdXz~Ka|2VjfIQ*$}uZDgQzDvqep)Fn!=S(V5PAFHm zpYiB^nUS~|p94wWz7c$-fDq_=EhbjMc0tZPm{_I2BUBp6TM*Cfuyom>?^AmAB`)=Q zrGAaTGET92g0Ve_2)^sl;K6kOcf=eIUV(JRj(F!{2d%sW<_q07YY$U7+PEJ^JxBpE zxKQ3pF@9FRqJH=ueva$SC_k*X{%c2%Mx6hq2Zi*s-WqSLe_s6r^mLH>!??Pjo^Imj zh4i#R^wjR(_Rlq>r!=gf7mtYihpC*%_})?C@qHDrH0klph`fNUpuBK=QU1U9CE(xw zcf@DM#?4gPdhvHKPu)y2=}WY1KIN-=ZosTPNcHvm0%kk=N3(s1@mS+U`qVGYwdu#JQOI_u|f2kHD-q>X-EV zu8Fa|^X=-O)ED{1_6jU=l=t~;J;CpE2K^VtDPU&EKJBj09Y6AKmR{mGV14vMI6pE_ zKb7ev{dae31KFCwczu1m$@b$+L&}&zC3j?Vp2U`P%aQ^XE{Wdjs-h`P%h<5A}R`a(}iww-(V8 z9rsnpm!%iYSU<_*gZ=cupQs-ibT&`j_*B?mSLB%(7FU72uQaV3=iWpY?lHW|`!3Y#tr6 z5TmMG`q1v_IVhy>I--x~e&+osHU|XJ;*TMWI`60bcL@)cnLiE-I=v|vbFWW_*-gi)cKE1aKtn}W>ut)D( z2-f#TRL_i@pVV`acpoLN=OI1)T};ir_kMNV66+)9b=ACZ<}T-Ysri@EAJJ(SQEAHs z?P{UC`d~KR7~m)okHB!In45MeQH*x8oQ7 z_cBxSFCzgare&$$)cnf@{*1s^2>dC5n+5)az`XvHn6^*g7J2@0fnO)^zY6?%f%&{d zYW^Dp{)jw(qrm?xFs~~mrhQo8R(ZZh;Hw4RE%2KJ=5y1j`MfZem^L8Kua)N+ffEA% zqrle*{0{3};po;c%C|Jwp~Wx)C_ zyUt&%*A%Gt|5^3ixoqFiFOhv%O5Hk-LqARMr`nH*bAadwz6a?h>#2cT)x+i8JkF-J z_j$bG-ra-+d@ry^;0A#=3(W0JZMVQYPNqii-pz6|x}JS^JGZZCqSC#)Q(*2_*9lFH z;?ef)QjhDSU&J0zumABmpmSOUJy$!2&*4AMU+edS6u*nYg3HKyC%!+1_0$#>89-k!NcRQ-+kLa}5VRzu~ z8L#fcnOcG2DN>j!=lxkzTSfR& z!2Wa+tmio0ferjT!}p_Q@RBH8ZhsfuUk&;Qs_W=FGL8qBfA~Hn*LTl}Q+>=!>HCzj z4ur2~;z*iC#=oXv9@R5j7%y&jAf-x&o;%0&1w7x4?eTg3cPW`atk2)2WPB0lCnWc1 z1%DUPmG2JXdy}{#tLx=;3yBQc&%*nQ7Q#Pp>30XwF8{(>w?X~ce%7&kr;@*uspp_0 z4u5~p&9GebJtY5oGddo<#;{QBk+*Gn+4{4#?5pK%<;JDnOn{L!FfS$SB;|r z;}RPdnCAto{|YSnBkNUmT+#P@ZXB>5{1VFN9`NVjPpN;me~`<8y;S0Q;!~IHgvbNl zTl>=XQ%wu|H(a+O{b=ES5ys23FkQj-I!z1n4eS0yr>SMWA^s*9?JSP0@@dn(zX;v$ zhK*_*?`QTO-GqIA;+gfY1b7SC3{eT+HKY>hJq_yGc}VL4&{NQRyb`lmAcgAv2fRb# z*|ot|#=l9&P4q?grD7~M$X5t2>`O9T#4*Z?c?B%M zdrurMnT;FR?(Ly&==UEE2IL+`ygTJNtSfK8IC3k)@ck^}L*G|-cd(q0&(Hn=^{a}W zr(^xpkDFn8n2vEA;AU#%d7nJ5C)i)-Xb`wZo;M4u@m`z2%%9o9{Gj~71cID6{>#xT zvV$Ba+V`KJ`$NdJo>H^=9@^z8-5PsyNmVEmHrbmUP*Bf*fk!nd44-+=ADyp zcU2O-$8uic$t>!Z=s}TwCW5~PNX2=BGYSWAv*Q<&zFHx;rxg1xzT-lo_(F_ zSI|85*JCOPw(EVF>g^Z2&>`XDd1Tl7BGf}YM2n)|F}3rFhkxV04`AngG+(XpI)L*x zMcoj8z3?aU_lj4-dd()jkMRNWt@}_&ht^vL^-L$%8*5xABJVh$#C}xXdr&T8jOS$Z zrY6wOhe-b8AJ_L&{jtY&E7j9+{jZ}aF_?FGdh&NrFXH?sJx9Az?02I!2l9^pgm;f| z+;AS{y~i*6o$meh*sYUX6{pfo9idY5gO2J}zQ2kJ_`Mjeuiu|A)eZ6-I)ckoH&N-X zs-n_VH&dx`yzkf9UwQt$mhFP(2|+)F;~oc0$j>dHUr`4(%izNO^d2ZxU-9|;XF-m~ zOMbC-1C6I?Y3H($$JcD{Vt8d6m&Ni6nQ!hrEYIk)dqD$$Z}=A_?&lFTgvZY#>IFt^ zhH;TR!OtVu{=4NePPNTUN34}f|9khF3D)@C9q8rf8NMGaTTk@3{XKNQcpky^^YRE> z42BEG5$83W8zMYwe7@(5^;oQ$1EFg`Djm?e3HykCRsWI1^R zPB^$w|0s`OaXIdKq!#*w$H6>eF6k$?({rh+|A+^lBw&phFNCQC5a;P_cNfbeST6Z_ zgp5c31|FAc8Bfh8s!>Zot{NYDd4a@-)%4hl4>iB&kUYZn>&5bdFG1A**O(Vn!1*N{ zzp2d&SpN#e`(OF&`0|2F;N2><>+T@i6?Y2qf_r}tdK6k-!1lq<3s_Fb=c}P#KknDO zpq}*I&kGs^*1Ul2o1Ygn({tnPkG2V{c|oVZf2q8H=kZkL1#bqq`gy?$$qQOZ4(^^V zDkIL-WFTxifcXaQJ4_FpGx-^8NuyrFe_&4zQG5S9)G*6;?LjI>i$5u!l=!pIzQZrz zY!S{|yMK*CQD1%lTTF%a9WXD!c9T61b(IxQ!MYB`W4aEZajv)CB5|p%x1d~7um;r; zYn3>U<)G(e_mjR>SKElIqW-BcrcDkhuC?1{$`Rp$5qJZ&5QkgJ6#Vj zYdOve##fr3qx>E|4E`A08+wk2^BVXU=U(zWG(!W-%BWn8Xx zs5G;WF#eSzT(%tMGB(U*;~^^D^}|HigyoYb7p4DkrhlUH$dki2Cn$%rMGnhF4)r33 z8ZKAHxooN8GFHiDV+ED=I>AX#gT9oZRfbab6>&Wk1*Pk!!S6e`q-8L!%c&Xs{<6yB zlg!67P+Yhs|Gfm>Z+GGQ5riiar!qA|^kSCig?xuOJ$n`Po8F$}a;^B`mEwO}INmUg z&Gg*Jxdm7cV0u&dRp1Hmzdla&^ZDP-_|kA#3-O(c|3Vxw#{A3wUc^?ygYgH>Z1?sC z#&<^(m8q^~F7LdOOSgr}^!AU@_@>u>l*`$BdE8eHa@o?yWn(Xwu^ujKH&Y4k`_jn5 zKI#b4t>-=6L9shqs`!2epYr<4X4qGPbIW=68_+Mw`n?_Zpq;bN=O2RnGrL3Zft)G; zpYEHW{p*9X0j}&TMPV-ps(^j}G&OKjBgA)dL65*<|K>72?|f6O#G86vtd{ja{iu=m zZTO#)JE_$fx>qJI-GHB+P>?vQM!+Uk*WjQ7+1#ewP`(>qYhn`JImM6Xo&y zVdl@p%)8qDj3Wd20*x=c4%jmxtiq&c{RcC zyQxyL-k7zlFJ>+4g{{9dE(ONnvp`S8VLv~bT^Pb~mT|>+xfe*m@!ekw?=KE=T&&|< zEdPqoYB}mJ^9$!C#f~z+;0Fa3eT!8PJQ`iY0lK<{uAN9%M(_)Lrb^U0gP9pfZ($&2G3L-V*p{0yhhMSm0KI-PEwaZ2}(>xSe1fZye8hc!KSOZZLR^KHo=f)6v#nx#H46~K6?|{Mn7`ET9Qb%{AUvru&RamQnBTH9 z2rrzM5t#KH&ebr#WpR04FEHx|oYNuL=D!_kBL5lWe>yYdUQEr$<{$Dvyv&JQ`gC8y z{A>KaoS%;sFPO$kqR)>PG#_YS{n*mW{G<-U$N|0b9R^d|$M;W@e!wYU1zo3!>hB7G zp5v9O5P5Dn45v8|7I|(N5m@B8o@7W<8 z@RkD0L%zGKc@M6;j7BGXSDX5W^IcrdKFDPr{u5bG<_TZ5gH$gyPxz|cE3oiYyPsfp z9wUNv1cv9To(vIe>q!srfqHUdN%iD;w*M2=6Yt!r%J*6J7c09(KaK#kXfJvgo=f1E z`kcLm>zT$rmPdMoZoqj=f>YB(zKz`ii+me53oP<&?BVv);?lmCVf7OOOjo|1|A6?= z--m|p5R#t5@0T(k!Ms}Z2Y#QJo_qN<+{eouDR8^Myn$=BcM_aW?|!D&ZKrnb zd&f!dmMX?KJI?*|v3{7A8os}_p6~BqyJA*0@cj!Izb~&r>OZx9Bhy_w0=qq6H`coX z9}#$izz*YG-z9K`z?}kD3e0(DYVrH1L-h-$b_2%;{Y(eiiTg1>uI-}xSP!Nmtp9Zq zjCs>XPv_VkTfq5fx}R>m->VN*;C>sy_IhDE;cex5Gq5$(TPtyl>@S(M61Qla+|Tup zE{wl)ol(~{(N4r5j)*!Rp^9)0hWK{pH4_ZyvZ+6JUX#EQ&&*>*HMJ5i&8_F>5-&kK zP3=tVah?wu&R0$BDS?~i`7a5^{Gb9b==&&UZ9V5BYva7Wl4+uP=~ztQW`R!yBVSP)-zAt!K-_o(~ z3C#MI*7M34);IqgH0zt5TQJ+@yh29ihn#1He2w)#wpZ{r6P%7cA~4VAbZn2nJfG9C zL4n()eV@Rc0{03m_9)gPu-L)aW`Q?I{ceG~rT>lEUSL<-3%o(#UTNPYa9rR{f%^n* z7q~{?Hi5-H#99R&r03~ai@~zy}4c7r0OKp+?|(c^((| zFu|ao0*?sHzfT6|AO)_H=Z@g36u3g*3V|yHb_5=l{y49K{6%2Sw;<1;`svuu+5TqY z0{=|l8i6@ab2IgVU-SP4f!UtGzNg@qJTTKBFxwY+-%RRr9tiXZEZ=?0u-!1RYh`}1 z-7vA&3oQ9h>{5Xx|B1au;6cLIA@85TzOdkbi#$Ip@T~+VGb2);-`96HkvRf;vVrS+ z=fk@NMyrhWX0yN=PxTON&+mAl^@Rb(kA4`}!_bcQeGmNjt~kj%I%7AjTiSUet!r8K zxCZb>oFzQIFWOzv^DY_J5rSW|yFDV0%_5%-G7q~2?j|@Ld(rOVjD+8z5PAGxvAaP# z8MLE8I~%maK|39^;}f&Hld+r24*7O7G2@1#xZi{JH2DLFt7dN|{dac_ak-k~OgrE0 zhS(A7r~e1=9P{~S%~Ka~+#3PJs8{1%FWcD7^EI$8=B=0It*6c7e!TZ6;SeqMmtlT! z9R?}z&pS5}e*e64v%tDu(8_Wuk0Tn)BLo)zR@+W6#Fut(^_;Og(8td+Y=5F< zCo^6B?DtngJ^`+;ztDDCqS!~}^;x}*KQ{p#o{Y~OGZ`?V4v*#_XXRji%XrlM-LgYodmxR^sou%?!)?i|&rS zEQgC(uiZ;zo~{4|fXls<3i!S815^*@ANR9!v%u{Ho0cXn8=JYTZROJ2=NhE?9kDLD zZ_B9{#?j8tF~3GTbY~6D-(HR@`}?>Y7^E`)eNm3*{r5#Vp7-Au<#;{+eNi4)|GW{8 ztM1GAaTCY)Hr;!R(0$o+yuTPY7f$gkt_lU-7xnH3-WT=m2i_N@`>;>L^3d;#>iH+Y ze;N##uB)INXMY0rF<{=~HQM{4?*xokALo_3tBds_sqwtbha~HV?k~DY))Q}EE6M$+ z{DR}E=KyqGV}GCfE!fxXO~jA?zGySSW@7`DdFKiGxgT7Qp}b7U&#N7&vcB>!N7Oa1m^QFZl+b>cA^8`TO`=XceUZYMuFv=M5a?Zf%OuCQLl2}7d=Gx;k^nf z)3Ku>&k=%cyyy?~yh)+=MPYk4=iCX&SL9vjebEnsT$A2+TBphP+=rMR*}sbr^(g;g zrYk1vW0^w&bA07y4hk&mV}cjX&5*yh`9Qm2>l^O#qkheJ68h2it7U&r_c;?YDmlNj zoADPg~#>goPu#7Q$B zqwK>M+pjHOHoo^MpI_?lZ`%AQpO+Tl^Mo8e-^}q<0Y1Bd&xo^wuuisOJ^#t-0{<#;i2WxZZk*;rMdu9HBIfCQ+Io}1VNB!Nz&vo4v$MG#7 zKmFbD>@H~!@dwpUGHmY`H^41y_f3$euE!_e$Mz57s+YoZvt7D}iu3$(!{L`JB7N+JDgRJ4yWN z>E#}11Q+T@a2!a_#807I-}Ym$Q=6)&zL9?2P4ECNJ$LDUpP_;I*hG)vc|G&HmgQyE zvYd>b=UmHju+NcT|DN7G#CVX7JAe*64}?8yeu($K{d52PLl!3kK0k>|{SJ-0w4QFb ztA?mV|BQDE&A(@19wnR$P{SSfUTJqv4YhNV2dGTW>!H%NTL(d(k)E$T3-s?kNb}17 zJ%!oaK68lf!+8OYm->epmha&#f%jHdKusK13~_NDbOr3w+hevqD!xj_w~O)F-v@0C z_#gUSsEiNVoz2h>()~K%TjL0OldCRCw z&4)<|SGw_RqHoRo(+OVJSkLs%e>K6T@d|;@5%?m3;jkQBrty4&Q*%)|p#Qb!5S)%( zMP+atY`cPS81i@K{}vh--bZ!^S&nYHgCAoSCw{rAQ>pchc%o^4^ z<6^JS@182shluk@p1)pPHp1iQ#cf9fKE(FP`VKaq`&0YO^7Fsr%yRbk9YxO8ce=6T zwDny+G^HK#eIlIKD*@L1M6}nb9DB`jgYTz|Ic{igblgyGpZF2*pQpdj8C>psmXmuT zN)uo?U*hiMd1zLOe0DaF{LD&`&(0=+9k%N`n+2{Ax$(Tv?;e>(pP&QXj4*x6g<2t3(d1To0!W42Ac* z)=`2O#u{e3*trNokgv|rbwqjJrJntxdtRKLr_Qh8@`5wCJcsQ}x{>z})7yFdI=!)q z#@Vc`XXZf-Y*$Fwq5I8svecdiQXUY{#ew~q4hmZD;&D8NAg3Dzf99d(|XdM(0vfY+jRfK z@aT-n!@!iLCrxU)ZbDO=x5b^CCGX=zbiL7K)d8v3&&kAP&c$$?EO#!3e!pJ9#)D)(jExt` zeis{0{GN^sebar8^U8rh@|vO&IF!ee6|_;%KcvP zsE}89yI(AB?o#S)>p{KmA{6(59xFF z8z{fTm+wD@d$8kCZJ+(XKhV2Z@QLl-AKrh%=iz=0>T@VJ5>GUJq`x8fSiA8+z0^0i z-tf?Eg3|uy=TUDkpO$)peuw@BXa%MRsnBCunz$B7EsU)qyFymx!$@u#`B>U-)5K!hIu z4tn(2y$anmtL**_^`B~U50?U=90;8QdL1BN#;jh@Ytrg{73dZ1(t2DTh!ms5p?{Ya_3x)9r=|V7 z1^uhd>3vp|?@Dpp{h2sP%>&aoiTm$a{5USYBooJc#92yD5-y4NR+?7_dY`4M*ca3L za`Wo*S7|&mY~R-{$k#K@E+5aNT_7EfG@f~VK$rA;AT}N^VSco z&JMXBSWfP~hb{l19*CRi0t?Py$XLO>lA!` z%MWbuE$6A9f-L%pfG-{rv+(F4hrLo9)(`PG%~`6<0;kespEtrU3yjAzD3~m0IO7clQXM z-6yUoIZrT|vA4afC-BoLzm&(@&dk_d%5mVQJKU{+!}_$~Nx%2z;Cr~_SKjKo6na*d z>lb=(ekt>ch8fW(tba@S%J-R(TO6ORmDJDSSBC$U6!3L4@*CX@?mzpvyk}gZ2{jVW z6-e3dC&TX4=Kf9Ihx7WS((MBox*fzmnvhSN53@Z|g6-|f!0#b^89GlBZkKdl*Q+!2 zO1XfKKHqv5`7@(-E3`Z8Z`h8*-w61a8T_I?O=+iuezw20+wGL?hfCkHR;**xkh`eg z%9GA{Ua3`kONK z|3>Q9afbATs6Z~ykPe6AOV$o>{}kn_A4y&+2!-AUbSPZQ7L}WD&?Dhp9A|F z)ZZ=Wd$yl#ryh2M?eyJ&@`=59gy?lt(EWt|9k8gdQQAxPbrgmy-=TOEBFlG4I#f{n z>S}r=j>UQDf1U9FMF#(Oesdnullgx3gHB54=auE=Rnks5x%?vNj(uwKzHi#jrENEQ z?J)Y?G^qHlv$Rv6$88F?zFEuR-W!1n`IWUJq$}xo^7lwbok!N4_*kLi(2UOS5O0yb z2Xqzw1a!50LRZTtbd~(jjN%vK^SR7;FlP4YRcMiTokIIjXqEDDL&558x4fSd?XdiZ z?R}T!|3UL+ml8XHxjuhULEmFuZ{@T+=KYq}`qMa?w(s``h5h(i#vZa?)K~Av?;C4! z!&~>L|F+G?ox`HPaf9B2hIppqZR3*S5jT9!@``8N@MX!@=JZ^9R4B$PDrE7__N%zx zn6F5?&xf4jAMRy;h{#QC?i8bw>P6gO^em{}0B@sHyUJs7ZnxF)#;=O>KRUdzPsixh zVt6>cnk?`1%v;{+ZT2M?&$9NVpZ^CNB5o@57#e=%0gmVfnj$fOgaz2KSP+3;yYMr8^`aZ&SSp$K|Ykw-Qh5 zN4dDk&Da-8`xfHm{USaMKP(@wJIwx|+>yrX9unHgOjus&6x<`Obh7U#lg?lP=E*Yx&pi2ta}r6OGYPEgNS^(>?P00i$NGyr$2}90 zE<|^cr$Bysze{`L@~Z7c1(#QCFV%l-$G=14``<%(8Ug)7zmD=sehp;gbcS|mJ356Q z&;1Hs=HC&11MsTN{f)d2>le6bc`WlEum8`!n)>&3C;B~yFVXUWK4s-F>*x2P1lxZ; z>|)ri$ZOz#I@~3I8!9M&>uT;H8$r*@jK5vNkErk*%Xe7*>6TY{iV7E5UhNb4&!6v8 zb-pKjE;D|%X6Q~j(az^l5BqZq@T|-IT<|OESEgJC_!XAhE9HKq<#rP*ozGuH{ViA? z_>Q0++L4Zo9cll*+L7DfQWB4s{Q!1E-OD2S(*G}4{_U8zmD3OUh1KduN>|mB^|rsG z8h!cw9pLZv_0n2>nYw||0;#|^Q!p%puETT zDkM$LH`{$Bbu~9ifoQwRa^HpJ^W?j*mlIpG2_(J}>@%kQ`!>4h`&@neqg~>U<1O*-CSfcuDYCQ^FW*$@ ze6!Kj#uq=|98D{~hgZK%>jNG5iwD%d!}l{~TuIM`nBN%KZ~716dA2;tvmc+8ehjd> zBdAa1E%YxwCiV47eNn;e+9~5gh*XcrMYMNHhr%@~U*c2REw6NseVw>b9s|Bb-&;H? zKBd?4YVYDxdMvN;_4t%7%bPtg{tTZrB+uhh`X%-6^O9XGf$x2)UbHFQ>Cdv|xXtxR z^%3hk3fFu$3WT=L_r4a|IFVTUq@%!M=y)`UCdxTwJ0*R1o|q2Ymi;WpB>E2gujw zo}12ZD0f$hoM^dZ9-7r34LeA^Qa`{+r%K;LOZ`Fh)0n3|C-JEgeb9I@@%i+3Xm{Ss zW-zX+{3UknG|hL{EEhdnDBgS7hgnaJ!WE=C@efvFenfvb{5}!e{nEogH{gkXlsmQe z1GAQ@zY(7M%g%SiU+mL%%FVm?VZRFLAGRBw`$>+%Ea zqSB>%S*yU;=3XfL3;n1{^0x`9e>~-HWD)sWb7?4ld)2S}Bapw>f~4Vmlqd6iK1h8! z5cmT=k0%}Z^Lb%7f70Y*fc3{U3({=c$77y@?o+HVk_r1n-3YXsV`B2s0 zbvmBle36V7ahs0E={?L-@_Z<{hk3^GO;VwcgHcW1@;3g}*!b=D

    x~Ovk@jsi$rk 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

    V@~Pi-qd(OTF)XKef*Orm(*o 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

    +N>ERj+sSD%|vYSlT{YU#IgWar)j@?LS-hQ9e&B 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%0Dl?YudLSNuSthAAhj% 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

    9#~M{RkFP@5pkti*!{22Mb9n%5rszbd^y|KZ1pM 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 z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ efB*pk1PBlyK!5-N0t5&UAV7cs0RsR31^zF;|KXPa literal 0 HcmV?d00001 diff --git a/solana/programs/matching-engine/tests/fixtures/mainnet_core_bridge.so b/solana/programs/matching-engine/tests/fixtures/mainnet_core_bridge.so new file mode 100644 index 0000000000000000000000000000000000000000..ef0a0bd62469326e41165a9ff42198c8da208031 GIT binary patch literal 973360 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�eizx=-PzXe4p==1OQqdeJ!7^z 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ά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

    AlW%Aq{*yX~E4cettn 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 literal 0 HcmV?d00001 diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index feb84106a..26c49f13b 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,31 +1,19 @@ use solana_program_test::{ProgramTest, ProgramTestContext, tokio}; use solana_sdk::{ - instruction::Instruction, pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction + pubkey::Pubkey, signature::{Keypair, Signer}, }; use std::rc::Rc; use std::cell::RefCell; -use solana_program::{bpf_loader_upgradeable, system_program}; -use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; -use anchor_lang::AccountDeserialize; - -use anchor_lang::{InstructionData, ToAccountMetas}; -use matching_engine::{ - accounts::Initialize, - InitializeArgs, - state::{ - // AuctionParameters, - Custodian, - AuctionConfig - }, -}; - mod utils; -use utils::token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}; +use utils::{router::add_local_router_endpoint_ix, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, Chain}; use utils::mint::MintFixture; -use utils::upgrade_manager::initialise_upgrade_manager; +use utils::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router}; use utils::airdrop::airdrop; - +use utils::initialize::initialize_program; +use utils::account_fixtures::FixtureAccounts; +use utils::router::add_cctp_router_endpoint_ix; +use utils::vaa::create_vaas_test; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { @@ -45,8 +33,6 @@ cfg_if::cfg_if! { } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; -// TODO: When modularising, impl function for the struct to add new solvers - pub struct Solver { pub actor: TestingActor, pub endpoint: Option, @@ -97,6 +83,7 @@ impl TestingActor { } } +/// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) pub struct TestingActors { pub owner: TestingActor, pub owner_assistant: TestingActor, @@ -153,15 +140,39 @@ impl TestingActors { actor.token_account = Some(usdc_ata); } } + + /// Add solvers to the testing actors + #[allow(dead_code)] + async fn add_solvers(&mut self, test_context: &Rc>, num_solvers: usize) { + for _ in 0..num_solvers { + let keypair = Rc::new(Keypair::new()); + let usdc_ata = create_token_account(test_context.clone(), &keypair, &USDC_MINT_ADDRESS).await; + airdrop(test_context, &keypair.pubkey(), 10000000000).await; + self.solvers.push(Solver::new(keypair.clone(), Some(usdc_ata), None)); + } + } } pub struct TestingContext { - pub program_data_account: Pubkey, + pub program_data_account: Pubkey, // Move this into something smarter pub testing_actors: TestingActors, pub test_context: Rc>, + pub fixture_accounts: Option, +} + +pub struct PreTestingContext { + pub program_test: ProgramTest, + pub testing_actors: TestingActors, + pub program_data_pubkey: Pubkey, + pub account_fixtures: FixtureAccounts, } -pub async fn setup_test_context() -> TestingContext { +/// Setup the test context +/// +/// # Returns +/// +/// A TestingContext struct containing the program data account, testing actors, test context, and fixture accounts +fn setup_program_test() -> PreTestingContext { let mut program_test = ProgramTest::new( "matching_engine", // Replace with your program name PROGRAM_ID, @@ -171,132 +182,134 @@ pub async fn setup_test_context() -> TestingContext { program_test.set_transaction_account_lock_limit(1000); // Setup Testing Actors - let mut testing_actors = TestingActors::new(); + let testing_actors = TestingActors::new(); // Initialise Upgrade Manager - let program_data = initialise_upgrade_manager(&mut program_test, &PROGRAM_ID, testing_actors.owner.pubkey()); + let program_data_pubkey = initialise_upgrade_manager(&mut program_test, &PROGRAM_ID, testing_actors.owner.pubkey()); + + // Initialise CCTP Token Messenger Minter + initialise_cctp_token_messenger_minter(&mut program_test); + + // Initialise Wormhole Core Bridge + initialise_wormhole_core_bridge(&mut program_test); + // Initialise CCTP Message Transmitter + initialise_cctp_message_transmitter(&mut program_test); + + // Initialise Local Token Router + initialise_local_token_router(&mut program_test); + + // Initialise Account Fixtures + let account_fixtures = FixtureAccounts::new(&mut program_test); + + // Add lookup table accounts + FixtureAccounts::add_lookup_table_hack(&mut program_test); + + PreTestingContext { program_test, testing_actors, program_data_pubkey, account_fixtures } +} + +async fn setup_testing_context(mut pre_testing_context: PreTestingContext) -> TestingContext { // Start and get test context - let test_context = Rc::new(RefCell::new(program_test.start_with_context().await)); + let test_context = Rc::new(RefCell::new(pre_testing_context.program_test.start_with_context().await)); // Airdrop to all actors - testing_actors.airdrop_all(&test_context).await; + pre_testing_context.testing_actors.airdrop_all(&test_context).await; // Create USDC mint let _mint_fixture = MintFixture::new_from_file(&test_context, USDC_MINT_FIXTURE_PATH); // Create USDC ATAs for all actors that need them - testing_actors.create_atas(&test_context).await; + pre_testing_context.testing_actors.create_atas(&test_context).await; - TestingContext { program_data_account: program_data, testing_actors, test_context } + TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, test_context, fixture_accounts: Some(pre_testing_context.account_fixtures) } } -pub struct InitializeFixture { - pub test_context: Rc>, - pub custodian: Custodian, +/// Test that the program is initialised correctly +#[tokio::test] +pub async fn test_initialize_program() { + + let pre_testing_context = setup_program_test(); + let testing_context = setup_testing_context(pre_testing_context).await; + + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + + // Check that custodian data corresponds to the expected values + initialize_fixture.verify_custodian(testing_context.testing_actors.owner.pubkey(), testing_context.testing_actors.owner_assistant.pubkey(), testing_context.testing_actors.fee_recipient.token_account.unwrap().address); } -async fn initialize_program(testing_context: &TestingContext) -> InitializeFixture { - let test_context = testing_context.test_context.clone(); - - let (custodian, _custodian_bump) = Pubkey::find_program_address( - &[Custodian::SEED_PREFIX], - &PROGRAM_ID, - ); +/// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains +#[tokio::test] +pub async fn test_cctp_token_router_endpoint_creation() { + let pre_testing_context = setup_program_test(); + let testing_context = setup_testing_context(pre_testing_context).await; - let (auction_config, _auction_config_bump) = Pubkey::find_program_address( - &[ - AuctionConfig::SEED_PREFIX, - &0u32.to_be_bytes(), - ], - &PROGRAM_ID, - ); - - // Create AuctionParameters - let auction_params = matching_engine::state::AuctionParameters { - user_penalty_reward_bps: 250_000, // 25% - initial_penalty_bps: 250_000, // 25% - duration: 2, - grace_period: 5, - penalty_period: 10, - min_offer_delta_bps: 20_000, // 2% - security_deposit_base: 4_200_000, - security_deposit_bps: 5_000, // 0.5% - }; - - // Create the instruction data - let ix_data = matching_engine::instruction::Initialize { - args: InitializeArgs { - auction_params, - }, - }; + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + + // Create a token router endpoint for the arbitrum chain + let arb_chain = Chain::Arbitrum; - // Get account metas - let accounts = Initialize { - owner: testing_context.testing_actors.owner.pubkey(), - custodian, - auction_config, - owner_assistant: testing_context.testing_actors.owner_assistant.pubkey(), - fee_recipient: testing_context.testing_actors.fee_recipient.pubkey(), - fee_recipient_token: testing_context.testing_actors.fee_recipient.token_account_address().unwrap(), - cctp_mint_recipient: CCTP_MINT_RECIPIENT, - usdc: matching_engine::accounts::Usdc{mint: USDC_MINT_ADDRESS}, - program_data: testing_context.program_data_account, - upgrade_manager_authority: common::UPGRADE_MANAGER_AUTHORITY, - upgrade_manager_program: common::UPGRADE_MANAGER_PROGRAM_ID, - bpf_loader_upgradeable_program: bpf_loader_upgradeable::id(), - system_program: system_program::id(), - token_program: spl_token::id(), - associated_token_program: spl_associated_token_account::id(), - }; + let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + let arb_remote_token_messenger = fixture_accounts.arbitrum_remote_token_messenger; + + let usdc_mint_address = USDC_MINT_ADDRESS; - // Create the instruction - let instruction = Instruction { - program_id: PROGRAM_ID, - accounts: accounts.to_account_metas(None), - data: ix_data.data(), - }; - - // Create and sign transaction - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&test_context.borrow().payer.pubkey()), - ); - transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], test_context.borrow().last_blockhash); + let arbitrum_token_router_endpoint = add_cctp_router_endpoint_ix( + &testing_context.test_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.custodian_address, + testing_context.testing_actors.owner.keypair().as_ref(), + PROGRAM_ID, + arb_remote_token_messenger, + usdc_mint_address, + arb_chain, + ).await; + assert_eq!(arbitrum_token_router_endpoint.info.chain, arb_chain.to_chain_id()); + + // Create a token router endpoint for the ethereum chain + let eth_chain = Chain::Ethereum; + let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; + + let _eth_token_router_endpoint = add_cctp_router_endpoint_ix( + &testing_context.test_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.custodian_address, + testing_context.testing_actors.owner.keypair().as_ref(), + PROGRAM_ID, + eth_remote_token_messenger, + usdc_mint_address, + eth_chain, + ).await; +} - // Process transaction - test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); +#[tokio::test] +pub async fn test_local_token_router_endpoint_creation() { + let pre_testing_context = setup_program_test(); + let testing_context = setup_testing_context(pre_testing_context).await; - // Verify the results - let custodian_account = test_context.borrow_mut().banks_client - .get_account(custodian) - .await - .unwrap() - .unwrap(); - - let custodian_data = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + let initialize_fixture: utils::initialize::InitializeFixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let _fixture_accounts: FixtureAccounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + + let usdc_mint_address = USDC_MINT_ADDRESS; - InitializeFixture { test_context, custodian: custodian_data } + let _local_token_router_endpoint = add_local_router_endpoint_ix( + &testing_context.test_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.custodian_address, + testing_context.testing_actors.owner.keypair().as_ref(), + PROGRAM_ID, + &usdc_mint_address, + ).await; } - + +// Test setting up vaas +// - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs #[tokio::test] -pub async fn test_initialize_program() { - - let testing_context = setup_test_context().await; - - let initialize_fixture = initialize_program(&testing_context).await; - - let custodian_data = initialize_fixture.custodian; - - // Verify owner is correct - assert_eq!(custodian_data.owner, testing_context.testing_actors.owner.pubkey()); - // Verify owner assistant is correct - assert_eq!(custodian_data.owner_assistant, testing_context.testing_actors.owner_assistant.pubkey()); - // Verify fee recipient token is correct - assert_eq!(custodian_data.fee_recipient_token, testing_context.testing_actors.fee_recipient.token_account.unwrap().address); - // Verify auction config id is 0 - assert_eq!(custodian_data.auction_config_id, 0); - // Verify next proposal id is 0 - assert_eq!(custodian_data.next_proposal_id, 0); +pub async fn test_setup_vaas() { + let mut pre_testing_context = setup_program_test(); + let vaas_test = create_vaas_test(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT); + let testing_context = setup_testing_context(pre_testing_context).await; + let _initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + vaas_test.0.first().unwrap().verify_vaas(&testing_context.test_context).await; + + // Try making initial offer } - diff --git a/solana/programs/matching-engine/tests/utils/account_fixtures.rs b/solana/programs/matching-engine/tests/utils/account_fixtures.rs new file mode 100644 index 000000000..591a5ed7a --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/account_fixtures.rs @@ -0,0 +1,139 @@ +use anchor_lang::prelude::{pubkey, Pubkey}; +use solana_program_test::ProgramTest; +use serde_json::Value; +use std::{fs, str::FromStr}; + +#[allow(dead_code)] +pub struct FixtureAccounts { + // Accounts/Core + pub core_bridge_config: Pubkey, + pub core_fee_collector: Pubkey, + pub core_guardian_set: Pubkey, + // Accounts/Message_Transmitter + pub message_transmitter_config: Pubkey, + // Accounts/Testnet + pub matching_engine_custodian: Pubkey, + pub token_router_custodian: Pubkey, + pub token_router_program: Pubkey, + // Accounts/Token_Messenger_Minter + pub arbitrum_remote_token_messenger: Pubkey, + pub ethereum_remote_token_messenger: Pubkey, + pub misconfigured_remote_token_messenger: Pubkey, + pub token_messenger: Pubkey, + pub token_minter: Pubkey, + pub usdc_custody_token: Pubkey, + pub usdc_local_token: Pubkey, // CCTP account (something that one of the programs use to track something) + pub usdc_token_pair: Pubkey, // Account that pairs links (in this case usdc solana) with usdc on another network +} + +impl FixtureAccounts { + /// Initialises all accounts in fixtures directory + /// + /// # Arguments + /// + /// * `program_test` - The program test instance + /// + /// # Returns + /// + /// A FixtureAccounts struct containing the addresses of all the accounts + pub fn new(program_test: &mut ProgramTest) -> Self { + // Since matching_engine_custodian is initialized by the test, we can just use the pubkey + Self { + core_bridge_config: add_account_from_file(program_test, "tests/fixtures/accounts/core_bridge/config.json").address, + core_fee_collector: add_account_from_file(program_test, "tests/fixtures/accounts/core_bridge/fee_collector.json").address, + core_guardian_set: add_account_from_file(program_test, "tests/fixtures/accounts/core_bridge/guardian_set_0.json").address, + message_transmitter_config: add_account_from_file(program_test, "tests/fixtures/accounts/message_transmitter/message_transmitter_config.json").address, + // matching_engine_custodian: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/matching_engine_custodian.json").address, + matching_engine_custodian: pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"), + token_router_custodian: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/token_router_custodian.json").address, + token_router_program: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/token_router_program_data_hacked.json").address, + arbitrum_remote_token_messenger: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json").address, + ethereum_remote_token_messenger: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json").address, + misconfigured_remote_token_messenger: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json").address, + token_messenger: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/token_messenger.json").address, + token_minter: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/token_minter.json").address, + usdc_custody_token: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json").address, + usdc_local_token: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json").address, + usdc_token_pair: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json").address, + } + } + /// Adds a lookup table to the program test + /// + /// # Arguments + /// + /// * `program_test` - The program test instance + pub fn add_lookup_table_hack(program_test: &mut ProgramTest) { + let filename = "tests/fixtures/lup.json"; + let account_fixture = read_account_from_file(filename); + program_test.add_account_with_file_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, filename); + } +} + +/// Adds an account from a JSON fixture file to the program test +/// +/// Loads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. +/// +/// # Arguments +/// +/// * `program_test` - The program test instance +/// * `filename` - The path to the JSON fixture file +fn add_account_from_file( + program_test: &mut ProgramTest, + filename: &str, +) -> AccountFixture { + // Parse the JSON file to an AccountFixture struct + let account_fixture = read_account_from_file(filename); + // Add the account to the program test + program_test.add_account_with_base64_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, &account_fixture.base_64_data); + account_fixture +} + +struct AccountFixture { + pub address: Pubkey, + pub owner: Pubkey, + pub lamports: u64, + pub base_64_data: String, +} + +// FIXME: This code is not being used, remove it + +/// Reads an account from a JSON fixture file +/// +/// Reads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. +/// +/// # Arguments +/// +/// * `filename` - The path to the JSON fixture file +/// +/// # Returns +/// +/// An AccountFixture struct containing the address, owner, lamports, and filename. +fn read_account_from_file( + filename: &str, +) -> AccountFixture { + // Read the JSON file + let data = fs::read_to_string(filename) + .expect(&format!("Unable to read file {}", filename)); + + // Parse the JSON + let json: Value = serde_json::from_str(&data) + .expect(&format!("Unable to parse JSON {}", filename)); + + // Extract the lamports value + let lamports = json["account"]["lamports"] + .as_u64() + .expect(&format!("lamports field not found or invalid {}", filename)); + + // Extract the address value + let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["pubkey"].as_str().expect("pubkey field not found or invalid")).expect("Pubkey field in file is not a valid pubkey"); + // Extract the owner address value + let owner: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["account"]["owner"].as_str().expect("owner field not found or invalid")).expect("Owner field in file is not a valid pubkey"); + + let base_64_data = json["account"]["data"][0].as_str().expect("data field not found or invalid"); + AccountFixture { + address, + owner, + lamports, + base_64_data: base_64_data.to_string(), + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index 8cf7e2c42..aef63450e 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -22,7 +22,6 @@ pub async fn airdrop( recipient: &Pubkey, amount: u64, ) { - println!("Airdropping {:?} with amount {:?}", recipient, amount); let mut ctx = test_context.borrow_mut(); // Create the transfer instruction with values from the context diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs new file mode 100644 index 000000000..7a409767d --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -0,0 +1,13 @@ +use anchor_lang::prelude::*; + +use matching_engine::state::Auction; +use matching_engine::instruction::{CreateNewAuctionHistory, CreateFirstAuctionHistory, PlaceInitialOfferCctp}; + + +pub async fn place_initial_offer( + testing_context: &mut TestingContext, + auction_config_id: u64, + fast_market_order: FastMarketOrder, +) { + +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 8645464c9..fc8a2b05c 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -1,10 +1,11 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; -use std::str::FromStr; // Program IDs -pub const CORE_BRIDGE_PID: Pubkey = Pubkey::from_str("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth").unwrap(); +pub const CORE_BRIDGE_PID: Pubkey = solana_program::pubkey!("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"); pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); +pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); +pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); /// Keypairs as base64 strings (taken from consts.ts in ts tests) // pub const PAYER_KEYPAIR_B64: &str = "cDfpY+VbRFXPPwouZwAx+ha9HqedkhqUr5vUaFa2ucAMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQ=="; @@ -13,35 +14,48 @@ pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgy // pub const PLAYER_ONE_KEYPAIR_B64: &str = "4STrqllKVVva0Fphqyf++6uGTVReATBe2cI26oIuVBft77CQP9qQrMTU1nM9ql0EnCpSgmCmm20m8khMo9WdPQ=="; /// Keypairs as base58 strings (taken from consts.ts in ts tests using a converter) +#[allow(dead_code)] pub const PAYER_KEYPAIR_B58: &str = "4NMwxzmYj2uvHuq8xoqhY8RXg0Pd5zkvmfWAL6YvbYFuViXVCBDK5Pru9GgqEVEZo6UXcPVH6rdR8JKgKxHGkXDp"; +#[allow(dead_code)] pub const OWNER_ASSISTANT_KEYPAIR_B58: &str = "2UbUgoidcNHxVEDG6ADNKGaGDqBTXTVw6B9pWvJtLNhbxcQDkdeEyBYBYYYxxDy92ckXUEaU9chWEGi5jc8Uc9e3"; +#[allow(dead_code)] pub const OWNER_KEYPAIR_B58: &str = "3M5rkG5DQVEGQFRtA1qruxPqJvYBbkGCdkCdB9ZjcnQnYL9ec8W78pLcQHVtjJzHP8phUXQ8V1SXbgZK9ZaFaS6U"; +#[allow(dead_code)] pub const PLAYER_ONE_KEYPAIR_B58: &str = "yqJrKqGqzuW6nEmfj62AgvZWqgGv9TqxfvPXiGvf8DxGDWz3UNkQdDfKDnBYpHQxPRVrYMupDKqbGVYHhfZApGb"; // Helper functions to get keypairs +#[allow(dead_code)] pub fn get_payer_keypair() -> Keypair { Keypair::from_base58_string(PAYER_KEYPAIR_B58) } +#[allow(dead_code)] pub fn get_owner_assistant_keypair() -> Keypair { Keypair::from_base58_string(OWNER_ASSISTANT_KEYPAIR_B58) } +#[allow(dead_code)] pub fn get_owner_keypair() -> Keypair { Keypair::from_base58_string(OWNER_KEYPAIR_B58) } +#[allow(dead_code)] pub fn get_player_one_keypair() -> Keypair { Keypair::from_base58_string(PLAYER_ONE_KEYPAIR_B58) } +// TODO: Remove these constants if not ever used // Other constants +#[allow(dead_code)] pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111111111111111111111111115"); +#[allow(dead_code)] pub const GUARDIAN_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; +#[allow(dead_code)] pub const USDC_MINT_ADDRESS: Pubkey = solana_program::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); +#[allow(dead_code)] pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; -// Chain to Domain mapping +// Chain to cctp domain mapping pub const CHAIN_TO_DOMAIN: &[(Chain, u32)] = &[ (Chain::Ethereum, 0), (Chain::Avalanche, 1), @@ -53,7 +67,7 @@ pub const CHAIN_TO_DOMAIN: &[(Chain, u32)] = &[ ]; // Enum for Chain types -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Chain { Ethereum, Avalanche, @@ -76,4 +90,20 @@ lazy_static::lazy_static! { m.insert(Chain::Polygon, vec![0xf7; 32]); m }; -} \ No newline at end of file +} + +// Chain ID mapping +impl Chain { + pub fn to_chain_id(&self) -> u16 { + match self { + Chain::Solana => 1, + Chain::Ethereum => 2, + Chain::Avalanche => 6, + Chain::Optimism => 24, + Chain::Arbitrum => 23, + Chain::Base => 30, + Chain::Polygon => 5, + } + } +} + diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/utils/initialize.rs new file mode 100644 index 000000000..4889befc2 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/initialize.rs @@ -0,0 +1,156 @@ +use solana_program_test::ProgramTestContext; +use solana_sdk::{ + instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction +}; +use std::rc::Rc; +use std::cell::RefCell; + +use solana_program::{bpf_loader_upgradeable, system_program}; +use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; +use anchor_lang::AccountDeserialize; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use matching_engine::{ + accounts::Initialize, + InitializeArgs, + state::{ + AuctionParameters, + Custodian, + AuctionConfig + }, +}; +use super::super::TestingContext; + +pub struct InitializeFixture { + pub test_context: Rc>, + pub custodian: Custodian, + pub custodian_address: Pubkey, +} + +#[derive(Debug, PartialEq, Eq)] +struct TestCustodian { + owner: Pubkey, + pending_owner: Option, + paused: bool, + paused_set_by: Pubkey, + owner_assistant: Pubkey, + fee_recipient_token: Pubkey, + auction_config_id: u32, + next_proposal_id: u64, +} + +impl From<&Custodian> for TestCustodian { + fn from(c: &Custodian) -> Self { + Self { + owner: c.owner, + pending_owner: c.pending_owner, + paused: c.paused, + paused_set_by: c.paused_set_by, + owner_assistant: c.owner_assistant, + fee_recipient_token: c.fee_recipient_token, + auction_config_id: c.auction_config_id, + next_proposal_id: c.next_proposal_id, + } + } +} + +impl InitializeFixture { + pub fn verify_custodian(&self, owner: Pubkey, owner_assistant: Pubkey, fee_recipient_token: Pubkey) { + let expected_custodian = TestCustodian { + owner, + pending_owner: None, + paused: false, + paused_set_by: owner, + owner_assistant, + fee_recipient_token, + auction_config_id: 0, + next_proposal_id: 0, + }; + + let actual_custodian = TestCustodian::from(&self.custodian); + assert_eq!(actual_custodian, expected_custodian); + } +} + +pub async fn initialize_program(testing_context: &TestingContext, program_id: Pubkey, usdc_mint_address: Pubkey, cctp_mint_recipient: Pubkey) -> InitializeFixture { + let test_context = testing_context.test_context.clone(); + + let (custodian, _custodian_bump) = Pubkey::find_program_address( + &[Custodian::SEED_PREFIX], + &program_id, + ); + + let (auction_config, _auction_config_bump) = Pubkey::find_program_address( + &[ + AuctionConfig::SEED_PREFIX, + &0u32.to_be_bytes(), + ], + &program_id, + ); + + // Create AuctionParameters + let auction_params = AuctionParameters { + user_penalty_reward_bps: 250_000, // 25% + initial_penalty_bps: 250_000, // 25% + duration: 2, + grace_period: 5, + penalty_period: 10, + min_offer_delta_bps: 20_000, // 2% + security_deposit_base: 4_200_000, + security_deposit_bps: 5_000, // 0.5% + }; + + // Create the instruction data + let ix_data = matching_engine::instruction::Initialize { + args: InitializeArgs { + auction_params, + }, + }; + + // Get account metas + let accounts = Initialize { + owner: testing_context.testing_actors.owner.pubkey(), + custodian, + auction_config, + owner_assistant: testing_context.testing_actors.owner_assistant.pubkey(), + fee_recipient: testing_context.testing_actors.fee_recipient.pubkey(), + fee_recipient_token: testing_context.testing_actors.fee_recipient.token_account_address().unwrap(), + cctp_mint_recipient: cctp_mint_recipient, + usdc: matching_engine::accounts::Usdc{mint: usdc_mint_address}, + program_data: testing_context.program_data_account, + upgrade_manager_authority: common::UPGRADE_MANAGER_AUTHORITY, + upgrade_manager_program: common::UPGRADE_MANAGER_PROGRAM_ID, + bpf_loader_upgradeable_program: bpf_loader_upgradeable::id(), + system_program: system_program::id(), + token_program: spl_token::id(), + associated_token_program: spl_associated_token_account::id(), + }; + + // Create the instruction + let instruction = Instruction { + program_id: program_id, + accounts: accounts.to_account_metas(None), + data: ix_data.data(), + }; + + // Create and sign transaction + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&test_context.borrow().payer.pubkey()), + ); + transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], test_context.borrow().last_blockhash); + + // Process transaction + test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + + // Verify the results + let custodian_account = test_context.borrow_mut().banks_client + .get_account(custodian.clone()) + .await + .unwrap() + .unwrap(); + + let custodian_data = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + + InitializeFixture { test_context, custodian: custodian_data, custodian_address: custodian } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/lookup_table.rs b/solana/programs/matching-engine/tests/utils/lookup_table.rs new file mode 100644 index 000000000..e22c95853 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/lookup_table.rs @@ -0,0 +1,113 @@ +use solana_program::address_lookup_table::{ + instruction::{create_lookup_table, extend_lookup_table}, + state::AddressLookupTable, +}; +use solana_program_test::ProgramTestContext; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, transaction::Transaction}; +use solana_program::pubkey; +use std::rc::Rc; +use std::cell::RefCell; + +// TODO: Figure out each of these addresses ... +struct LookupTableAddresses { + pub core_bridge_config: Pubkey, + pub core_emitter_sequence: Pubkey, // Nothing needs to be done: will be created from first message by bridge + pub core_fee_collector: Pubkey, + pub core_bridge_program: Pubkey, // ?? + pub matching_engine_program: Pubkey, // Will need to be loaded as a .so + pub system_program: Pubkey, + pub rent: Pubkey, + pub clock: Pubkey, + pub custodian: Pubkey, + pub event_authority: Pubkey, // Derive key for it + pub cctp_mint_recipient: Pubkey, // Initialised from Initialize + pub token_messenger: Pubkey, + pub token_minter: Pubkey, + pub token_messenger_minter_sender_authority: Pubkey, + pub token_messenger_minter_program: Pubkey, + pub message_transmitter_authority: Pubkey, + pub message_transmitter_config: Pubkey, + pub message_transmitter_program: Pubkey, + pub token_program: Pubkey, + pub mint: Pubkey, // (USDC mint address) + pub local_token: Pubkey, + pub token_messenger_minter_custody_token: Pubkey, // usdc custody token + pub token_messenger_minter_event_authority: Pubkey, // Derive key for it + pub message_transmitter_event_authority: Pubkey, // Derive key for it +} + +impl LookupTableAddresses { + pub fn new(matching_engine_program: Pubkey, custodian: Pubkey, cctp_mint_recipient: Pubkey, mint: Pubkey) -> Self { + Self { + core_bridge_config: pubkey!(""), + core_emitter_sequence: pubkey!(""), + core_fee_collector: pubkey!(""), + core_bridge_program: pubkey!(""), + matching_engine_program, + system_program: solana_program::system_program::ID, + rent: solana_program::sysvar::rent::ID, + clock: solana_program::sysvar::clock::ID, + custodian, + event_authority: pubkey!(""), + cctp_mint_recipient, + token_messenger: pubkey!(""), + token_minter: pubkey!(""), + token_messenger_minter_sender_authority: pubkey!(""), + token_messenger_minter_program: pubkey!(""), + message_transmitter_authority: pubkey!(""), + message_transmitter_config: pubkey!(""), + message_transmitter_program: pubkey!(""), + token_program: anchor_spl::token::ID, + mint, + local_token: pubkey!(""), + token_messenger_minter_custody_token: pubkey!(""), + token_messenger_minter_event_authority: pubkey!(""), + message_transmitter_event_authority: pubkey!(""), + } + } +} + +async fn setup_lookup_table( + test_context: &Rc>, + addresses: Vec, +) -> Pubkey { + let mut ctx = test_context.borrow_mut(); + + // Get recent slot + let slot = ctx.banks_client.get_root_slot().await.unwrap(); + + // Create lookup table + let (lookup_table_address, create_ix) = create_lookup_table( + ctx.payer.pubkey(), // Authority + ctx.payer.pubkey(), // Payer + slot, // Recent slot + ); + + // Process create instruction + let create_tx = Transaction::new_signed_with_payer( + &[create_ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(create_tx).await.unwrap(); + + // Extend lookup table with addresses + let extend_ix = extend_lookup_table( + lookup_table_address, + ctx.payer.pubkey(), // Authority + Some(ctx.payer.pubkey()), // Payer (optional) + addresses, + ); + + // Process extend instruction + let extend_tx = Transaction::new_signed_with_payer( + &[extend_ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(extend_tx).await.unwrap(); + + lookup_table_address +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index ce6213925..84fa2f984 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -3,6 +3,14 @@ pub mod token_account; pub mod mint; -pub mod upgrade_manager; +pub mod program_fixtures; pub mod airdrop; -// pub mod constants; +pub mod constants; +// pub mod lookup_table; +// pub mod transfer_ownership; +pub mod account_fixtures; +pub mod initialize; +pub mod router; +pub mod vaa; +pub mod auction; +pub use constants::*; \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/upgrade_manager.rs b/solana/programs/matching-engine/tests/utils/program_fixtures.rs similarity index 59% rename from solana/programs/matching-engine/tests/utils/upgrade_manager.rs rename to solana/programs/matching-engine/tests/utils/program_fixtures.rs index 0d4f31bd7..75412d120 100644 --- a/solana/programs/matching-engine/tests/utils/upgrade_manager.rs +++ b/solana/programs/matching-engine/tests/utils/program_fixtures.rs @@ -2,7 +2,8 @@ use solana_program_test::ProgramTest; use solana_sdk::pubkey::Pubkey; use solana_program::bpf_loader_upgradeable; -// TODO: Use this function in the test +use super::{TOKEN_ROUTER_PID, CORE_BRIDGE_PID, CCTP_TOKEN_MESSENGER_MINTER_PID, CCTP_MESSAGE_TRANSMITTER_PID}; + fn get_program_data(owner: Pubkey) -> Vec { let state = solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { slot: 0, @@ -37,4 +38,24 @@ pub fn initialise_upgrade_manager(program_test: &mut ProgramTest, program_id: &P program_test.add_program("upgrade_manager", common::UPGRADE_MANAGER_PROGRAM_ID, None); program_data_pubkey +} + +pub fn initialise_cctp_token_messenger_minter(program_test: &mut ProgramTest) { + let program_id = CCTP_TOKEN_MESSENGER_MINTER_PID; + program_test.add_program("mainnet_cctp_token_messenger_minter", program_id, None); +} + +pub fn initialise_wormhole_core_bridge(program_test: &mut ProgramTest) { + let program_id = CORE_BRIDGE_PID; + program_test.add_program("mainnet_core_bridge", program_id, None); +} + +pub fn initialise_cctp_message_transmitter(program_test: &mut ProgramTest) { + let program_id = CCTP_MESSAGE_TRANSMITTER_PID; + program_test.add_program("mainnet_cctp_message_transmitter", program_id, None); +} + +pub fn initialise_local_token_router(program_test: &mut ProgramTest) { + let program_id = TOKEN_ROUTER_PID; + program_test.add_program("token_router", program_id, None); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs new file mode 100644 index 000000000..f34a52b1f --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -0,0 +1,232 @@ +// Add methods for adding endpoints to the program test + +use anchor_lang::prelude::*; +use anchor_lang::Discriminator; +use anchor_lang::{InstructionData, ToAccountMetas}; +use common::wormhole_cctp_solana::cctp::token_messenger_minter_program::RemoteTokenMessenger; +use matching_engine::state::Custodian; +use matching_engine::state::EndpointInfo; +use matching_engine::LOCAL_CUSTODY_TOKEN_SEED_PREFIX; +use solana_program_test::ProgramTestContext; +use std::rc::Rc; +use std::cell::RefCell; +use matching_engine::instruction::{AddCctpRouterEndpoint, AddLocalRouterEndpoint}; +use matching_engine::accounts::{AddCctpRouterEndpoint as AddCctpRouterEndpointAccounts, AddLocalRouterEndpoint as AddLocalRouterEndpointAccounts, Admin, CheckedCustodian, LocalTokenRouter}; +use matching_engine::AddCctpRouterEndpointArgs; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::{Signer, Keypair}; +use solana_sdk::transaction::Transaction; +use matching_engine::state::RouterEndpoint; +use super::constants::*; +use super::token_account::create_token_account_for_pda; + +fn generate_admin(owner_or_assistant: Pubkey, custodian: Pubkey) -> Admin { + let checked_custodian = CheckedCustodian { custodian }; + Admin { + owner_or_assistant, + custodian: checked_custodian, + } +} + +#[allow(dead_code)] +async fn print_account_discriminator( + test_context: &Rc>, + address: &Pubkey, +) { + println!("Printing account discriminator for address: {:?}", address); + let account = test_context.borrow_mut() + .banks_client + .get_account(*address) + .await + .unwrap() + .expect("Account not found"); + + println!("Account data: {:?}", account.data); + + let account_owner = account.owner; + println!("Account owner: {:?}", account_owner); + + // Get first 8 bytes (discriminator) + let discriminator = &account.data[..8]; + println!("Account discriminator: {:?}", discriminator); + + // Compare with expected discriminator (WARNING: ASSUMPTION) + let expected = RemoteTokenMessenger::discriminator(); + println!("Expected discriminator: {:?}", expected); + +} + +/// A struct representing an endpoint info for testing purposes +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TestEndpointInfo { + pub chain: u16, + pub address: [u8; 32], + pub mint_recipient: [u8; 32], + pub protocol: matching_engine::state::MessageProtocol, +} + +impl From<&EndpointInfo> for TestEndpointInfo { + fn from(endpoint_info: &EndpointInfo) -> Self { + Self { chain: endpoint_info.chain, address: endpoint_info.address, mint_recipient: endpoint_info.mint_recipient, protocol: endpoint_info.protocol } + } +} + +impl TestEndpointInfo { + pub fn new(chain: Chain, address: &Pubkey, mint_recipient: Option<&Pubkey>, protocol: matching_engine::state::MessageProtocol) -> Self { + if let Some(mint_recipient) = mint_recipient { + Self { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: mint_recipient.to_bytes(), protocol: protocol } + } else { + Self { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: address.to_bytes(), protocol: protocol } + } + } +} + +/// A struct representing a router endpoint for testing purposes +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TestRouterEndpoint { + pub bump: u8, + pub info: TestEndpointInfo, +} + +impl From<&RouterEndpoint> for TestRouterEndpoint { + fn from(router_endpoint: &RouterEndpoint) -> Self { + Self { bump: router_endpoint.bump, info: (&router_endpoint.info).into() } + } +} + +impl TestRouterEndpoint { + pub fn verify_endpoint_info(&self, chain: Chain, address: &Pubkey, mint_recipient: Option<&Pubkey>, protocol: matching_engine::state::MessageProtocol) { + let expected_info = TestEndpointInfo::new(chain, address, mint_recipient, protocol); + assert_eq!(self.info, expected_info); + } +} + +pub async fn add_cctp_router_endpoint_ix( + test_context: &Rc>, + admin_owner_or_assistant: Pubkey, + admin_custodian: Pubkey, + admin_keypair: &Keypair, + program_id: Pubkey, + remote_token_messenger: Pubkey, + usdc_mint_address: Pubkey, + chain: Chain, +) -> TestRouterEndpoint { + let admin = generate_admin(admin_owner_or_assistant, admin_custodian); + let usdc = matching_engine::accounts::Usdc{mint: usdc_mint_address}; + + // This should be equivalent to writeUint16BigEndian + let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); + let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); + + + let local_custody_token_address = Pubkey::find_program_address(&[LOCAL_CUSTODY_TOKEN_SEED_PREFIX, &encoded_chain], &program_id).0; + + let accounts = AddCctpRouterEndpointAccounts { + payer: test_context.borrow().payer.pubkey(), + admin, + router_endpoint: router_endpoint_address, + local_custody_token: local_custody_token_address, + usdc, + remote_token_messenger, + token_program: anchor_spl::token::ID, + system_program: anchor_lang::system_program::ID, + }; + + let registered_token_router_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ix_data = AddCctpRouterEndpoint { + args: AddCctpRouterEndpointArgs { + chain: chain.to_chain_id(), + cctp_domain: CHAIN_TO_DOMAIN[chain as usize].1, + address: registered_token_router_address, + mint_recipient: None, + }, + }.data(); + + let instruction = Instruction { + program_id: program_id, + accounts: accounts.to_account_metas(None), + data: ix_data, + }; + + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&test_context.borrow().payer.pubkey()), + ); + // TODO: Figure out who the signers are + transaction.sign(&[&test_context.borrow().payer, &admin_keypair], test_context.borrow().last_blockhash); + + test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + + let endpoint_account = test_context.borrow_mut().banks_client + .get_account(router_endpoint_address) + .await + .unwrap() + .unwrap(); + + let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); + + let test_router_endpoint = TestRouterEndpoint::from(&endpoint_data); + test_router_endpoint.verify_endpoint_info(chain, &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { domain: CHAIN_TO_DOMAIN[chain as usize].1 }); + test_router_endpoint +} + +pub async fn add_local_router_endpoint_ix( + test_context: &Rc>, + admin_owner_or_assistant: Pubkey, + admin_custodian: Pubkey, + admin_keypair: &Keypair, + program_id: Pubkey, + usdc_mint_address: &Pubkey, +) -> TestRouterEndpoint { + let admin = generate_admin(admin_owner_or_assistant, admin_custodian); + + let token_router_program = TOKEN_ROUTER_PID; + let token_router_emitter = Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &token_router_program).0; + let token_router_mint_recipient = create_token_account_for_pda(test_context, &token_router_emitter, usdc_mint_address).await; + // Create the local token router + let local_token_router = LocalTokenRouter { + token_router_program, + token_router_emitter: token_router_emitter.clone(), + token_router_mint_recipient, + }; + let chain = Chain::Solana; + let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); + let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); + + // Create the router endpoint + let accounts = AddLocalRouterEndpointAccounts { + payer: test_context.borrow().payer.pubkey(), + admin, + router_endpoint: router_endpoint_address, + local: local_token_router, + system_program: anchor_lang::system_program::ID, + }; + + let ix_data = AddLocalRouterEndpoint {}.data(); + + let instruction = Instruction { + program_id: program_id, + accounts: accounts.to_account_metas(None), + data: ix_data, + }; + + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&test_context.borrow().payer.pubkey()), + ); + transaction.sign(&[&test_context.borrow().payer, &admin_keypair], test_context.borrow().last_blockhash); + + test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + + let endpoint_account = test_context.borrow_mut().banks_client + .get_account(router_endpoint_address) + .await + .unwrap() + .unwrap(); + + let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); + + let test_router_endpoint = TestRouterEndpoint::from(&endpoint_data); + test_router_endpoint.verify_endpoint_info(chain, &token_router_emitter, Some(&token_router_mint_recipient), matching_engine::state::MessageProtocol::Local { program_id: token_router_program }); + test_router_endpoint +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/programs/matching-engine/tests/utils/token_account.rs index 471d36055..009dfb858 100644 --- a/solana/programs/matching-engine/tests/utils/token_account.rs +++ b/solana/programs/matching-engine/tests/utils/token_account.rs @@ -1,9 +1,8 @@ use solana_sdk::{program_pack::Pack, transaction::Transaction, pubkey::Pubkey, signature::Keypair, signer::Signer}; use anchor_spl::token::spl_token; use anchor_spl::associated_token::spl_associated_token_account; -use solana_program_test::{ProgramTest, ProgramTestContext}; -use serde_json::Value; -use std::{cell::RefCell, fs, rc::Rc, str::FromStr}; +use solana_program_test::ProgramTestContext; +use std::{cell::RefCell, fs, rc::Rc}; pub struct TokenAccountFixture { @@ -76,6 +75,37 @@ pub async fn create_token_account( } } +pub async fn create_token_account_for_pda( + test_context: &Rc>, + pda: &Pubkey, // The PDA that will own the token account + mint: &Pubkey, // The mint (USDC in your case) +) -> Pubkey { + let mut ctx = test_context.borrow_mut(); + + // Get the ATA address + let ata = anchor_spl::associated_token::get_associated_token_address(&pda, mint); + + // Create the create_ata instruction + let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( + &ctx.payer.pubkey(), // Funding account + pda, // Account that will own the token account + mint, // Token mint (USDC) + &spl_token::id(), // Token program + ); + + // Create and send transaction + let transaction = Transaction::new_signed_with_payer( + &[create_ata_ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(transaction).await.unwrap(); + + ata +} + /// Reads a keypair from a JSON fixture file /// /// Reads the JSON file and parses it into a Value object that is used to extract the keypair. @@ -95,72 +125,4 @@ pub fn read_keypair_from_file(filename: &str) -> Keypair { // Create keypair from bytes Keypair::from_bytes(&bytes) .expect("Bytes must form a valid keypair") -} - -// FIXME: This does not work, using the function in the mint.rs file instead -/// Adds an account from a JSON fixture file to the program test -/// -/// Loads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. -/// -/// # Arguments -/// -/// * `program_test` - The program test instance -/// * `filename` - The path to the JSON fixture file -#[allow(dead_code, unused_variables)] -pub fn add_account_from_file( - program_test: &mut ProgramTest, - filename: &str, -) { - // Parse the JSON file to an AccountFixture struct - let account_fixture = read_account_from_file(filename); - // Add the account to the program test - program_test.add_account_with_file_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, filename) -} - -#[allow(dead_code, unused_variables)] -struct AccountFixture { - pub address: Pubkey, - pub owner: Pubkey, - pub lamports: u64, -} - -// FIXME: This code is not being used, remove it - -/// Reads an account from a JSON fixture file -/// -/// Reads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. -/// -/// # Arguments -/// -/// * `filename` - The path to the JSON fixture file -/// -/// # Returns -/// -/// An AccountFixture struct containing the address, owner, lamports, and filename. -fn read_account_from_file( - filename: &str, -) -> AccountFixture { - // Read the JSON file - let data = fs::read_to_string(filename) - .expect("Unable to read file"); - - // Parse the JSON - let json: Value = serde_json::from_str(&data) - .expect("Unable to parse JSON"); - - // Extract the lamports value - let lamports = json["account"]["lamports"] - .as_u64() - .expect("lamports field not found or invalid"); - - // Extract the address value - let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["pubkey"].as_str().expect("pubkey field not found or invalid")).expect("Pubkey field in file is not a valid pubkey"); - // Extract the owner address value - let owner: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["account"]["owner"].as_str().expect("owner field not found or invalid")).expect("Owner field in file is not a valid pubkey"); - - AccountFixture { - address, - owner, - lamports, - } } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/transfer_ownership.rs b/solana/programs/matching-engine/tests/utils/transfer_ownership.rs new file mode 100644 index 000000000..980c4dcbd --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/transfer_ownership.rs @@ -0,0 +1,32 @@ +use anchor_lang::prelude::*; +use anchor_lang::{InstructionData, ToAccountMetas}; + +pub fn transfer_ownership( + program_id: Pubkey, + custodian: Pubkey, + cctp_mint_recipient: Pubkey, + mint: Pubkey, +) -> Instruction { + // TODO: Implement this +} + +fn create_submit_ownership_transfer_ix( + program_id: Pubkey, + custodian: Pubkey, + sender: Pubkey, + new_owner: Pubkey, +) -> Instruction { + + let accounts = matching_engine::accounts::SubmitOwnershipTransferRequest { + admin: sender, + new_owner, + }; + + let ix_data = matching_engine::instruction::SubmitOwnershipTransferRequest{}.data(); + + Instruction { + program_id, + accounts: accounts.to_account_metas(None), + data: ix_data, + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs new file mode 100644 index 000000000..f849335b2 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -0,0 +1,352 @@ +use anchor_lang::prelude::*; +use common::messages::{FastMarketOrder, SlowOrderResponse}; +use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; +use common::wormhole_cctp_solana::wormhole::VaaAccount; // TODO: Remove this if not needed +use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload +use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; // TODO: Remove this if not needed +use matching_engine::instruction::PlaceInitialOfferCctp; + +use super::constants::Chain; +use super::CHAIN_TO_DOMAIN; + +use borsh::{ + BorshDeserialize, + BorshSerialize, +}; +use serde::{ + Deserialize, + Serialize, +}; +use solana_sdk::account::Account; +use super::constants::CORE_BRIDGE_PID; +use solana_program_test::{ProgramTest, ProgramTestContext}; +use solana_program::keccak; +use std::cell::RefCell; +use std::rc::Rc; + +pub trait DataDiscriminator { + const DISCRIMINATOR: &'static [u8]; +} + +#[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct PostedVaaData { + /// Header of the posted VAA + pub vaa_version: u8, + + /// Level of consistency requested by the emitter + pub consistency_level: u8, + + /// Time the vaa was submitted + pub vaa_time: u32, + + /// Account where signatures are stored + pub vaa_signature_account: Pubkey, + + /// Time the posted message was created + pub submission_time: u32, + + /// Unique nonce for this message + pub nonce: u32, + + /// Sequence number of this message + pub sequence: u64, + + /// Emitter of the message + pub emitter_chain: u16, + + /// Emitter of the message + pub emitter_address: [u8; 32], + + /// Message payload + pub payload: Vec, +} + +impl DataDiscriminator for PostedVaaData { + const DISCRIMINATOR: &'static [u8] = b"vaa\x01"; +} + +impl PostedVaaData { + pub fn new( + chain: Chain, payload: Vec, emitter_address: impl ToBytes, sequence: u64, nonce: u32 + ) -> Self { + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + let emitter_chain = chain.to_chain_id(); + Self { + vaa_version: 1, + consistency_level: 1, + vaa_time: timestamp, + vaa_signature_account: Pubkey::new_unique(), + submission_time: 0, + nonce, + sequence, + emitter_chain, + emitter_address: emitter_address.to_bytes(), + payload: payload.to_vec(), + } + } + + pub fn message_hash(&self) -> keccak::Hash { + keccak::hashv(&[ + self.vaa_time.to_be_bytes().as_ref(), + self.nonce.to_be_bytes().as_ref(), + self.emitter_chain.to_be_bytes().as_ref(), + &self.emitter_address, + &self.sequence.to_be_bytes(), + &[self.consistency_level], + self.payload.as_ref(), + ]) + } + + pub fn create_vaa_account(&self, program_test: &mut ProgramTest) -> Pubkey { + let vaa_hash = self.message_hash(); + let vaa_hash_as_slice = vaa_hash.as_ref(); + let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; + + let vaa_data_serialized = serialize_with_discriminator(self).unwrap(); + let lamports = solana_sdk::rent::Rent::default().minimum_balance(vaa_data_serialized.len()); + let vaa_account = Account { + lamports: lamports, + data: vaa_data_serialized, + owner: CORE_BRIDGE_PID, + executable: false, + rent_epoch: u64::MAX, + }; + program_test.add_account(vaa_address, vaa_account); + vaa_address + } +} + +pub fn deserialize_with_discriminator(data: &[u8]) -> Option { + let mut discriminant = [0u8; 4]; + discriminant.copy_from_slice(&data[..4]); + if discriminant != T::DISCRIMINATOR { + return None; + } + let mut data = data[4..].to_vec(); + let message = T::try_from_slice(&mut data); + match message { + Ok(message) => Some(message), + Err(_) => None, + } +} + +pub fn serialize_with_discriminator(message: &T) -> Result> +where + T: BorshSerialize + DataDiscriminator +{ + let mut data = Vec::new(); + data.extend_from_slice(T::DISCRIMINATOR); + message.serialize(&mut data)?; + Ok(data) +} + +pub struct TestFastTransfer { + pub token_mint: Pubkey, + pub source_address: ChainAddress, + pub refund_address: ChainAddress, + pub destination_address: ChainAddress, + pub cctp_nonce: u32, + pub sequence: u64, + pub deposit_vaa_pubkey: Pubkey, + pub fast_transfer_vaa_pubkey: Pubkey, + pub deposit_vaa_data: PostedVaaData, + pub fast_transfer_vaa_data: PostedVaaData, +} + +impl TestFastTransfer { + pub fn new(program_test: &mut ProgramTest, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> Self { + let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(program_test, token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient); + let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(program_test, start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence); + Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa_pubkey, fast_transfer_vaa_pubkey, deposit_vaa_data, fast_transfer_vaa_data } + } + + pub async fn verify_vaas(&self, test_context: &Rc>) { + let expected_deposit_vaa = self.deposit_vaa_data.clone(); + let expected_fast_transfer_vaa = self.fast_transfer_vaa_data.clone(); + { + let deposit_vaa = test_context.borrow_mut().banks_client.get_account(self.deposit_vaa_pubkey.clone()).await.unwrap(); + assert!(deposit_vaa.is_some(), "Deposit VAA not found"); + let deposit_vaa = deserialize_with_discriminator::(&deposit_vaa.unwrap().data).unwrap(); + assert_eq!(deposit_vaa, expected_deposit_vaa); + } + + { + let fast_transfer_vaa = test_context.borrow_mut().banks_client.get_account(self.fast_transfer_vaa_pubkey.clone()).await.unwrap(); + assert!(fast_transfer_vaa.is_some(), "Fast transfer VAA not found"); + let fast_transfer_vaa = deserialize_with_discriminator::(&fast_transfer_vaa.unwrap().data).unwrap(); + assert_eq!(fast_transfer_vaa, expected_fast_transfer_vaa); + } + } +} + +pub fn create_deposit_message(program_test: &mut ProgramTest, token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> (Pubkey, PostedVaaData) { + + let slow_order_response = SlowOrderResponse { + base_fee: 0, + }; + // Implements TypePrefixedPayload + let deposit = Deposit { + token_address: token_mint.to_bytes(), + amount: ruint::aliases::U256::from(100), + source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain as usize].1, + destination_cctp_domain: CHAIN_TO_DOMAIN[destination_address.chain as usize].1, + cctp_nonce, + burn_source: source_address.address.to_bytes(), // Token router address + mint_recipient: cctp_mint_recipient.to_bytes(), // Mint recipient program id + payload: WriteableBytes::new(slow_order_response.to_vec()), + }; + + // Sequece == nonce in this case, since only vaas we are submitting are fast transfers + let posted_vaa_data = PostedVaaData::new(source_address.chain, deposit.to_vec(), source_address.address, sequence, cctp_nonce as u32); + let vaa_address = posted_vaa_data.create_vaa_account(program_test); + (vaa_address, posted_vaa_data) +} + +pub fn create_fast_transfer_message(program_test: &mut ProgramTest, start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64) -> (Pubkey, PostedVaaData) { + // If start timestamp is not provided, set the deadline to 0 + let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); + // Implements TypePrefixedPayload + let fast_market_order = FastMarketOrder { + amount_in: 100, + min_amount_out: 100, + target_chain: destination_address.chain.to_chain_id(), + redeemer: destination_address.address.to_bytes(), + sender: source_address.address.to_bytes(), + refund_address: refund_address.address.to_bytes(), // Not used so can be all zeros + max_fee: 100, // USDC max fee + init_auction_fee: 10, // USDC init auction fee (the first person to verify a vaa and start an auction will get this fee) so at least rent + deadline, // If dealine is 0 then there is no deadline + redeemer_message: WriteableBytes::new(vec![]), + }; + + let posted_vaa_data = PostedVaaData::new(source_address.chain, fast_market_order.to_vec(), source_address.address, sequence, cctp_nonce as u32); + let vaa_address = posted_vaa_data.create_vaa_account(program_test); + (vaa_address, posted_vaa_data) +} + +pub struct TestFastTransfers(pub Vec); + +impl TestFastTransfers { + pub fn new() -> Self { + Self(Vec::new()) + } + + #[allow(dead_code)] + pub fn len(&self) -> usize { + self.0.len() + } + + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer + pub fn add_ft(&mut self, program_test: &mut ProgramTest, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey) { + let sequence = self.len() as u64; + let cctp_nonce = sequence; + let test_fast_transfer = TestFastTransfer::new(program_test, start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient); + self.0.push(test_fast_transfer); + } +} + +pub fn create_vaas_test(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey) -> TestFastTransfers { + let mut test_fast_transfers = TestFastTransfers::new(); + let source_address = ChainAddress::new_unique(Chain::Solana); + let destination_address = ChainAddress::new_unique(Chain::Ethereum); + let refund_address = source_address.clone(); + test_fast_transfers.add_ft(program_test, start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); + test_fast_transfers +} + +pub trait ToBytes { + fn to_bytes(&self) ->[u8; 32]; +} + +#[derive(Debug, Clone)] +pub enum TestPubkey { + Solana(Pubkey), + Evm(EvmAddress), +} + +impl ToBytes for TestPubkey { + fn to_bytes(&self) -> [u8; 32] { + match self { + TestPubkey::Solana(pubkey) => pubkey.to_bytes(), + TestPubkey::Evm(evm_address) => evm_address.to_bytes(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvmAddress([u8; 20]); + +#[allow(dead_code)] +impl EvmAddress { + pub fn new(bytes: [u8; 20]) -> Self { + Self(bytes) + } + + pub fn from_hex(hex: &str) -> Option { + let hex = hex.strip_prefix("0x").unwrap_or(hex); + let bytes = hex::decode(hex).ok()?; + if bytes.len() != 20 { + return None; + } + let mut array = [0u8; 20]; + array.copy_from_slice(&bytes); + Some(Self(array)) + } + + pub fn as_bytes(&self) -> &[u8; 20] { + &self.0 + } + + pub fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self.0)) + } + + pub fn new_unique() -> Self { + let (_secp_secret_key, secp_pubkey) = secp256k1::generate_keypair(&mut secp256k1::rand::rngs::OsRng); + // Get uncompressed public key bytes (65 bytes: prefix + x + y) + let uncompressed = secp_pubkey.serialize_uncompressed(); + // Hash with Keccak-256 removing the prefix + let hash = keccak::hashv(&[&uncompressed[1..]]); + // Address is the last 20 bytes of the hash + let address: [u8; 20] = hash.as_ref()[12..].try_into().unwrap(); + Self(address) + } +} + +impl ToBytes for EvmAddress { + fn to_bytes(&self) -> [u8; 32] { + // Pad the evm address with 12 zero bytes + let mut bytes = vec![0u8; 12]; + bytes.extend_from_slice(&self.0); + bytes.try_into().unwrap() + } +} + +#[derive(Clone)] +pub struct ChainAddress { + pub chain: Chain, + pub address: TestPubkey, +} + +impl ChainAddress { + pub fn new_unique(chain: Chain) -> Self { + match chain { + Chain::Solana => Self { chain, address: TestPubkey::Solana(Pubkey::new_unique()) }, + Chain::Ethereum => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Arbitrum => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Avalanche => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Optimism => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Polygon => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Base => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + } + } +} + From 412b6ff85f1cb917ddb3cd6f96e518e0dc8e86de Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 14 Feb 2025 17:29:30 +0000 Subject: [PATCH 006/112] working improve offer and initial offer --- .../tests/initialize_integration_tests.rs | 306 ++++-------------- .../matching-engine/tests/utils/airdrop.rs | 31 +- .../matching-engine/tests/utils/auction.rs | 237 +++++++++++++- .../matching-engine/tests/utils/constants.rs | 55 +++- .../matching-engine/tests/utils/initialize.rs | 25 +- .../matching-engine/tests/utils/mod.rs | 3 +- .../matching-engine/tests/utils/router.rs | 120 ++++++- .../matching-engine/tests/utils/setup.rs | 221 +++++++++++++ .../tests/utils/token_account.rs | 2 +- .../matching-engine/tests/utils/vaa.rs | 65 +++- 10 files changed, 786 insertions(+), 279 deletions(-) create mode 100644 solana/programs/matching-engine/tests/utils/setup.rs diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 26c49f13b..5cd40d7ec 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,19 +1,14 @@ -use solana_program_test::{ProgramTest, ProgramTestContext, tokio}; -use solana_sdk::{ - pubkey::Pubkey, signature::{Keypair, Signer}, -}; -use std::rc::Rc; -use std::cell::RefCell; +use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; mod utils; -use utils::{router::add_local_router_endpoint_ix, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, Chain}; -use utils::mint::MintFixture; -use utils::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router}; -use utils::airdrop::airdrop; +use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; +use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test, get_router_endpoint_address}; use utils::initialize::initialize_program; use utils::account_fixtures::FixtureAccounts; -use utils::router::add_cctp_router_endpoint_ix; -use utils::vaa::create_vaas_test; +use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; +use utils::setup::{PreTestingContext, TestingContext}; +use utils::vaa::{create_vaas_test, create_vaas_test_with_chain_and_address}; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { @@ -33,203 +28,12 @@ cfg_if::cfg_if! { } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; -pub struct Solver { - pub actor: TestingActor, - pub endpoint: Option, -} - -impl Solver { - pub fn new(keypair: Rc, token_account: Option, endpoint: Option) -> Self { - Self { actor: TestingActor::new(keypair, token_account), endpoint } - } - - pub fn get_endpoint(&self) -> Option { - self.endpoint.clone() - } - - pub fn keypair(&self) -> Rc { - self.actor.keypair.clone() - } - - pub fn pubkey(&self) -> Pubkey { - self.actor.keypair.pubkey() - } -} - -pub struct TestingActor { - pub keypair: Rc, - pub token_account: Option, -} - -impl std::fmt::Debug for TestingActor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", self.keypair.pubkey(), self.token_account) - } -} - -impl TestingActor { - pub fn new(keypair: Rc, token_account: Option) -> Self { - Self { keypair, token_account } - } - pub fn pubkey(&self) -> Pubkey { - self.keypair.pubkey() - } - pub fn keypair(&self) -> Rc { - self.keypair.clone() - } - - pub fn token_account_address(&self) -> Option { - self.token_account.as_ref().map(|t| t.address) - } -} - -/// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) -pub struct TestingActors { - pub owner: TestingActor, - pub owner_assistant: TestingActor, - pub fee_recipient: TestingActor, - pub relayer: TestingActor, - pub solvers: Vec, - pub liquidator: TestingActor, -} - -impl TestingActors { - pub fn new() -> Self { - let owner_kp = Rc::new(read_keypair_from_file(OWNER_KEYPAIR_PATH)); - let owner = TestingActor::new(owner_kp.clone(), None); - let owner_assistant = TestingActor::new(owner_kp.clone(), None); - let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None); - let relayer = TestingActor::new(Rc::new(Keypair::new()), None); - // TODO: Change player 1 solver to use the keyfile - let mut solvers = vec![]; - solvers.extend(vec![ - Solver::new(Rc::new(Keypair::new()), None, None), - Solver::new(Rc::new(Keypair::new()), None, None), - Solver::new(Rc::new(Keypair::new()), None, None), - ]); - let liquidator = TestingActor::new(Rc::new(Keypair::new()), None); - Self { owner, owner_assistant, fee_recipient, relayer, solvers, liquidator } - } - - pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { - let mut actors = Vec::new(); - actors.push(&mut self.fee_recipient); - for solver in &mut self.solvers { - actors.push(&mut solver.actor); - } - actors.push(&mut self.liquidator); - actors - } - - /// Transfer Lamports to Executors - async fn airdrop_all(&self, test_context: &Rc>) { - airdrop(test_context, &self.owner.pubkey(), 10000000000).await; - airdrop(test_context, &self.owner_assistant.pubkey(), 10000000000).await; - airdrop(test_context, &self.fee_recipient.pubkey(), 10000000000).await; - airdrop(test_context, &self.relayer.pubkey(), 10000000000).await; - for solver in self.solvers.iter() { - airdrop(test_context, &solver.pubkey(), 10000000000).await; - } - airdrop(test_context, &self.liquidator.pubkey(), 10000000000).await; - } - - /// Set up ATAs for Various Owners - async fn create_atas(&mut self, test_context: &Rc>) { - for actor in self.token_account_actors() { - let usdc_ata = create_token_account(test_context.clone(), &actor.keypair(), &USDC_MINT_ADDRESS).await; - actor.token_account = Some(usdc_ata); - } - } - - /// Add solvers to the testing actors - #[allow(dead_code)] - async fn add_solvers(&mut self, test_context: &Rc>, num_solvers: usize) { - for _ in 0..num_solvers { - let keypair = Rc::new(Keypair::new()); - let usdc_ata = create_token_account(test_context.clone(), &keypair, &USDC_MINT_ADDRESS).await; - airdrop(test_context, &keypair.pubkey(), 10000000000).await; - self.solvers.push(Solver::new(keypair.clone(), Some(usdc_ata), None)); - } - } -} - -pub struct TestingContext { - pub program_data_account: Pubkey, // Move this into something smarter - pub testing_actors: TestingActors, - pub test_context: Rc>, - pub fixture_accounts: Option, -} - -pub struct PreTestingContext { - pub program_test: ProgramTest, - pub testing_actors: TestingActors, - pub program_data_pubkey: Pubkey, - pub account_fixtures: FixtureAccounts, -} - -/// Setup the test context -/// -/// # Returns -/// -/// A TestingContext struct containing the program data account, testing actors, test context, and fixture accounts -fn setup_program_test() -> PreTestingContext { - let mut program_test = ProgramTest::new( - "matching_engine", // Replace with your program name - PROGRAM_ID, - None, - ); - program_test.set_compute_max_units(1000000000); - program_test.set_transaction_account_lock_limit(1000); - - // Setup Testing Actors - let testing_actors = TestingActors::new(); - - // Initialise Upgrade Manager - let program_data_pubkey = initialise_upgrade_manager(&mut program_test, &PROGRAM_ID, testing_actors.owner.pubkey()); - - // Initialise CCTP Token Messenger Minter - initialise_cctp_token_messenger_minter(&mut program_test); - - // Initialise Wormhole Core Bridge - initialise_wormhole_core_bridge(&mut program_test); - - // Initialise CCTP Message Transmitter - initialise_cctp_message_transmitter(&mut program_test); - - // Initialise Local Token Router - initialise_local_token_router(&mut program_test); - - // Initialise Account Fixtures - let account_fixtures = FixtureAccounts::new(&mut program_test); - - // Add lookup table accounts - FixtureAccounts::add_lookup_table_hack(&mut program_test); - - PreTestingContext { program_test, testing_actors, program_data_pubkey, account_fixtures } -} - -async fn setup_testing_context(mut pre_testing_context: PreTestingContext) -> TestingContext { - // Start and get test context - let test_context = Rc::new(RefCell::new(pre_testing_context.program_test.start_with_context().await)); - - // Airdrop to all actors - pre_testing_context.testing_actors.airdrop_all(&test_context).await; - - // Create USDC mint - let _mint_fixture = MintFixture::new_from_file(&test_context, USDC_MINT_FIXTURE_PATH); - - // Create USDC ATAs for all actors that need them - pre_testing_context.testing_actors.create_atas(&test_context).await; - - TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, test_context, fixture_accounts: Some(pre_testing_context.account_fixtures) } -} - /// Test that the program is initialised correctly #[tokio::test] pub async fn test_initialize_program() { - let pre_testing_context = setup_program_test(); - let testing_context = setup_testing_context(pre_testing_context).await; + let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; @@ -240,61 +44,44 @@ pub async fn test_initialize_program() { /// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains #[tokio::test] pub async fn test_cctp_token_router_endpoint_creation() { - let pre_testing_context = setup_program_test(); - let testing_context = setup_testing_context(pre_testing_context).await; + let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - - // Create a token router endpoint for the arbitrum chain - let arb_chain = Chain::Arbitrum; let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); let arb_remote_token_messenger = fixture_accounts.arbitrum_remote_token_messenger; + let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; let usdc_mint_address = USDC_MINT_ADDRESS; - let arbitrum_token_router_endpoint = add_cctp_router_endpoint_ix( + let token_router_endpoints = create_cctp_router_endpoints_test( &testing_context.test_context, testing_context.testing_actors.owner.pubkey(), - initialize_fixture.custodian_address, - testing_context.testing_actors.owner.keypair().as_ref(), - PROGRAM_ID, + initialize_fixture.get_custodian_address(), arb_remote_token_messenger, - usdc_mint_address, - arb_chain, - ).await; - assert_eq!(arbitrum_token_router_endpoint.info.chain, arb_chain.to_chain_id()); - - // Create a token router endpoint for the ethereum chain - let eth_chain = Chain::Ethereum; - let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; - - let _eth_token_router_endpoint = add_cctp_router_endpoint_ix( - &testing_context.test_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.custodian_address, - testing_context.testing_actors.owner.keypair().as_ref(), - PROGRAM_ID, eth_remote_token_messenger, usdc_mint_address, - eth_chain, + testing_context.testing_actors.owner.keypair(), + PROGRAM_ID, ).await; + + assert_eq!(token_router_endpoints.len(), 2); } #[tokio::test] pub async fn test_local_token_router_endpoint_creation() { - let pre_testing_context = setup_program_test(); - let testing_context = setup_testing_context(pre_testing_context).await; + let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; let initialize_fixture: utils::initialize::InitializeFixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - let _fixture_accounts: FixtureAccounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); let usdc_mint_address = USDC_MINT_ADDRESS; let _local_token_router_endpoint = add_local_router_endpoint_ix( &testing_context.test_context, testing_context.testing_actors.owner.pubkey(), - initialize_fixture.custodian_address, + initialize_fixture.get_custodian_address(), testing_context.testing_actors.owner.keypair().as_ref(), PROGRAM_ID, &usdc_mint_address, @@ -305,11 +92,52 @@ pub async fn test_local_token_router_endpoint_creation() { // - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs #[tokio::test] pub async fn test_setup_vaas() { - let mut pre_testing_context = setup_program_test(); - let vaas_test = create_vaas_test(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT); - let testing_context = setup_testing_context(pre_testing_context).await; - let _initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - vaas_test.0.first().unwrap().verify_vaas(&testing_context.test_context).await; + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let first_test_ft = vaas_test.0.first().unwrap(); + first_test_ft.verify_vaas(&testing_context.test_context).await; + // Get the fixture accounts + let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); // Try making initial offer + let fast_vaa = first_test_ft.fast_transfer_vaa.get_vaa_pubkey(); + let usdc_mint_address = USDC_MINT_ADDRESS; + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = create_all_router_endpoints_test( + &testing_context.test_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + fixture_accounts.arbitrum_remote_token_messenger, + fixture_accounts.ethereum_remote_token_messenger, + usdc_mint_address, + testing_context.testing_actors.owner.keypair(), + PROGRAM_ID, + ).await; + let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; + let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + let _local_endpoint_address = router_endpoints.solana.endpoint_address; + // TODO: Get auction pubkey + + let solver = testing_context.testing_actors.solvers[0].clone(); + let auction_accounts = AuctionAccounts::new( + fast_vaa, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + arb_endpoint_address, // From router endpoint pubkey + eth_endpoint_address, // To router endpoint pubkey + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + ); + + let fast_market_order = first_test_ft.fast_transfer_vaa.clone(); + + let initial_offer_fixture = place_initial_offer(&testing_context.test_context, &auction_accounts, fast_market_order, testing_context.testing_actors.owner.keypair(), PROGRAM_ID).await; + initial_offer_fixture.verify_initial_offer(&testing_context.test_context).await; + + let improved_offer_fixture = improve_offer(&testing_context.test_context, initial_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; + // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index aef63450e..bc441062d 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -1,3 +1,6 @@ +use anchor_lang::prelude::*; +use anchor_lang::ToAccountInfo; +use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; use std::rc::Rc; use std::cell::RefCell; @@ -8,6 +11,7 @@ use solana_sdk::{ }; use solana_sdk::transaction::Transaction; +use super::constants; /// Airdrops SOL to a given recipient /// @@ -40,4 +44,29 @@ pub async fn airdrop( ); ctx.banks_client.process_transaction(tx).await.unwrap(); -} \ No newline at end of file +} + +pub async fn airdrop_usdc( + test_context: &Rc>, + recipient_ata: &Pubkey, + owner: &Pubkey, + amount: u64, +) { + let usdc_mint_address = constants::USDC_MINT; + let mint_to_ix = spl_token::instruction::mint_to( + &spl_token::ID, + &usdc_mint_address, + recipient_ata, + &test_context.borrow().payer.pubkey(), + &[], + amount + ).expect("Failed to create mint to instruction"); + let tx = Transaction::new_signed_with_payer( + &[mint_to_ix.clone()], + Some(&test_context.borrow().payer.pubkey()), + &[&test_context.borrow().payer], + test_context.borrow().last_blockhash, + ); + + test_context.borrow_mut().banks_client.process_transaction(tx).await.unwrap(); +} diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 7a409767d..92e919720 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,13 +1,238 @@ use anchor_lang::prelude::*; -use matching_engine::state::Auction; -use matching_engine::instruction::{CreateNewAuctionHistory, CreateFirstAuctionHistory, PlaceInitialOfferCctp}; +use matching_engine::state::{Auction, AuctionConfig, AuctionInfo}; +use matching_engine::instruction::{CreateNewAuctionHistory, CreateFirstAuctionHistory, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, ImproveOffer as ImproveOfferIx}; +use matching_engine::accounts::{ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc}; +use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; +use solana_sdk::instruction::Instruction; +use std::cell::RefCell; +use std::rc::Rc; +use solana_program_test::ProgramTestContext; +use solana_sdk::transaction::Transaction; +use solana_sdk::signature::{Keypair, Signer}; +use common::TRANSFER_AUTHORITY_SEED_PREFIX; +use anchor_lang::InstructionData; +use solana_sdk::program_pack::Pack; +use super::setup::Solver; +use super::vaa::TestVaa; +pub struct AuctionAccounts { + pub fast_vaa: Pubkey, + pub offer_token: Pubkey, + pub solver: Solver, + pub auction_config: Pubkey, + pub from_router_endpoint: Pubkey, + pub to_router_endpoint: Pubkey, + pub custodian: Pubkey, + pub usdc_mint: Pubkey, +} + +impl AuctionAccounts { + pub fn new(fast_vaa: Pubkey, solver: Solver, auction_config: Pubkey, from_router_endpoint: Pubkey, to_router_endpoint: Pubkey, custodian: Pubkey, usdc_mint_address: Pubkey) -> Self { + Self { + fast_vaa, + offer_token: solver.token_account_address().unwrap(), + solver, + auction_config, + from_router_endpoint, + to_router_endpoint, + custodian, + usdc_mint: usdc_mint_address, + } + } +} + +pub struct AuctionOfferFixture { + pub auction_address: Pubkey, + pub auction_custody_token_address: Pubkey, + pub offer_price: u64, + pub offer_token: Pubkey, +} + +impl AuctionOfferFixture { + // TODO: Figure this out + pub async fn verify_initial_offer(&self, testing_context: &Rc>) { + let auction_account = testing_context.borrow_mut().banks_client.get_account(self.auction_address).await.unwrap().expect("Failed to get auction account"); + let mut data_ref = auction_account.data.as_ref(); + let auction_account_data : Auction = AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + println!("Auction account: {:?}", auction_account_data); + let auction_info = auction_account_data.info.unwrap(); + let expected_auction_info = AuctionInfo { + config_id: 0, + custody_token_bump: 254, // TODO: Figure this out + vaa_sequence: 0, + source_chain: 23, + best_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata + initial_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata + start_slot: 1, + amount_in: 1000, + security_deposit: 1_004__200_005, + offer_price: 1__000_000, + redeemer_message_len: 0, + destination_asset_info: None, + }; + assert_eq!(auction_info.config_id, expected_auction_info.config_id); + assert_eq!(auction_info.vaa_sequence, expected_auction_info.vaa_sequence); + assert_eq!(auction_info.source_chain, expected_auction_info.source_chain); + assert_eq!(auction_info.start_slot, expected_auction_info.start_slot); + assert_eq!(auction_info.amount_in, expected_auction_info.amount_in); + assert_eq!(auction_info.security_deposit, expected_auction_info.security_deposit); + assert_eq!(auction_info.offer_price, expected_auction_info.offer_price); + assert_eq!(auction_info.redeemer_message_len, expected_auction_info.redeemer_message_len); + } +} pub async fn place_initial_offer( - testing_context: &mut TestingContext, - auction_config_id: u64, - fast_market_order: FastMarketOrder, -) { + testing_context: &Rc>, + accounts: &AuctionAccounts, + fast_market_order: TestVaa, + owner_keypair: Rc, + program_id: Pubkey, +) -> AuctionOfferFixture { + + let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id).0; + let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; + let initial_offer_ix = PlaceInitialOfferCctpIx { + offer_price: 1__000_000, + }; + + let fast_order_path = FastOrderPath { + fast_vaa: LiquidityLayerVaa { + vaa: fast_market_order.vaa_pubkey, + }, + path: LiveRouterPath { + from_endpoint: LiveRouterEndpoint { + endpoint: accounts.from_router_endpoint, + }, + to_endpoint: LiveRouterEndpoint { + endpoint: accounts.to_router_endpoint, + }, + }, + }; + { + let fast_vaa_account = testing_context.borrow_mut().banks_client.get_account(fast_market_order.vaa_pubkey).await.unwrap().expect("Failed to get fast vaa account"); + println!("Fast VAA Account: {:?}", fast_vaa_account); + println!("fast vaa owner: {:?}", fast_vaa_account.owner); + } + + let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; + let transfer_authority = Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &initial_offer_ix.offer_price.to_be_bytes()], &program_id).0; + accounts.solver.approve_usdc(testing_context, &transfer_authority, 420_000__000_000).await; + let custodian = CheckedCustodian { + custodian: accounts.custodian, + }; + let initial_offer_accounts = PlaceInitialOfferCctpAccounts { + payer: owner_keypair.pubkey(), + transfer_authority, + custodian, + auction_config: accounts.auction_config, + fast_order_path, + auction: auction_address, + offer_token: accounts.offer_token, + auction_custody_token: auction_custody_token_address, + usdc: Usdc { mint: accounts.usdc_mint }, + system_program: anchor_lang::system_program::ID, + token_program: anchor_spl::token::ID, + program: program_id, + event_authority, + }; + + let mut account_metas = initial_offer_accounts.to_account_metas(None); + for meta in account_metas.iter_mut() { + if meta.pubkey == accounts.offer_token { + meta.is_writable = true; + } + } + let initial_offer_ix_anchor = Instruction{ + program_id: program_id, + accounts: account_metas, + data: initial_offer_ix.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[initial_offer_ix_anchor], + Some(&owner_keypair.pubkey()), + &[&owner_keypair], + testing_context.borrow().last_blockhash, + ); + + testing_context.borrow_mut().banks_client.process_transaction(tx).await.expect("Failed to place initial offer"); + + AuctionOfferFixture { + auction_address, + auction_custody_token_address, + offer_price: initial_offer_ix.offer_price, + offer_token: accounts.offer_token, + } +} + + + +pub async fn improve_offer( + testing_context: &Rc>, + initial_offer_fixture: AuctionOfferFixture, + owner_keypair: Rc, + program_id: Pubkey, + solver: Solver, + auction_config: Pubkey, +) -> AuctionOfferFixture { + + let auction_address = initial_offer_fixture.auction_address; + let auction_custody_token_address = initial_offer_fixture.auction_custody_token_address; + + // Decrease the offer by 0.5 usdc + let improve_offer_ix = ImproveOfferIx { + offer_price: initial_offer_fixture.offer_price - 500_000, + }; + + let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; + let transfer_authority = Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &improve_offer_ix.offer_price.to_be_bytes()], &program_id).0; + solver.approve_usdc(testing_context, &transfer_authority, 420_000__000_000).await; + let offer_token = solver.token_account_address().unwrap(); + + let active_auction = ActiveAuction { + auction: auction_address, + custody_token: auction_custody_token_address, + config: auction_config, + best_offer_token: initial_offer_fixture.offer_token, + }; + let improve_offer_accounts = ImproveOfferAccounts { + transfer_authority, + active_auction, + offer_token, + token_program: anchor_spl::token::ID, + event_authority, + program: program_id, + }; + + let mut account_metas = improve_offer_accounts.to_account_metas(None); + for meta in account_metas.iter_mut() { + if meta.pubkey == initial_offer_fixture.offer_token { + meta.is_writable = true; + } + } + + // TODO: Figure out better name for this + let improve_offer_ix_anchor = Instruction { + program_id: program_id, + accounts: account_metas, + data: improve_offer_ix.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[improve_offer_ix_anchor], + Some(&owner_keypair.pubkey()), + &[&owner_keypair], + testing_context.borrow().last_blockhash, + ); + + testing_context.borrow_mut().banks_client.process_transaction(tx).await.expect("Failed to improve offer"); + + AuctionOfferFixture { + auction_address, + auction_custody_token_address, + offer_token, + offer_price: improve_offer_ix.offer_price, + } } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index fc8a2b05c..2b3d3f5b9 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -1,8 +1,59 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; +use solana_program::pubkey; // Program IDs -pub const CORE_BRIDGE_PID: Pubkey = solana_program::pubkey!("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"); + +cfg_if::cfg_if! { + if #[cfg(feature = "mainnet")] { + /// Core Bridge program ID on Solana mainnet. + pub const CORE_BRIDGE_PID: Pubkey = pubkey!("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"); + pub const CORE_BRIDGE_FEE_COLLECTOR: Pubkey = pubkey!("9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy"); + pub const CORE_BRIDGE_CONFIG: Pubkey = pubkey!("2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn"); + + /// Token Bridge program ID on Solana mainnet. + pub const TOKEN_BRIDGE_PID: Pubkey = pubkey!("wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"); + pub const TOKEN_BRIDGE_EMITTER_AUTHORITY: Pubkey = pubkey!("Gv1KWf8DT1jKv5pKBmGaTmVszqa56Xn8YGx2Pg7i7qAk"); + pub const TOKEN_BRIDGE_CUSTODY_AUTHORITY: Pubkey = pubkey!("GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m"); + pub const TOKEN_BRIDGE_MINT_AUTHORITY: Pubkey = pubkey!("BCD75RNBHrJJpW4dXVagL5mPjzRLnVZq4YirJdjEYMV7"); + pub const TOKEN_BRIDGE_TRANSFER_AUTHORITY: Pubkey = pubkey!("7oPa2PHQdZmjSPqvpZN7MQxnC7Dcf3uL4oLqknGLk2S3"); + + /// USDC mint address found on Solana mainnet. + pub const USDC_MINT: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); + } else if #[cfg(feature = "testnet")] { + /// Core Bridge program ID on Solana devnet. + pub const CORE_BRIDGE_PID: Pubkey = pubkey!("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"); + pub const CORE_BRIDGE_FEE_COLLECTOR: Pubkey = pubkey!("7s3a1ycs16d6SNDumaRtjcoyMaTDZPavzgsmS3uUZYWX"); + pub const CORE_BRIDGE_CONFIG: Pubkey = pubkey!("6bi4JGDoRwUs9TYBuvoA7dUVyikTJDrJsJU1ew6KVLiu"); + + /// Token Bridge program ID on Solana devnet. + pub const TOKEN_BRIDGE_PID: Pubkey = pubkey!("DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe"); + pub const TOKEN_BRIDGE_EMITTER_AUTHORITY: Pubkey = pubkey!("4yttKWzRoNYS2HekxDfcZYmfQqnVWpKiJ8eydYRuFRgs"); + pub const TOKEN_BRIDGE_CUSTODY_AUTHORITY: Pubkey = pubkey!("H9pUTqZoRyFdaedRezhykA1aTMq7vbqRHYVhpHZK2QbC"); + pub const TOKEN_BRIDGE_MINT_AUTHORITY: Pubkey = pubkey!("rRsXLHe7sBHdyKU3KY3wbcgWvoT1Ntqudf6e9PKusgb"); + pub const TOKEN_BRIDGE_TRANSFER_AUTHORITY: Pubkey = pubkey!("3VFdJkFuzrcwCwdxhKRETGxrDtUVAipNmYcLvRBDcQeH"); + + /// USDC mint address found on Solana devnet. + pub const USDC_MINT: Pubkey = pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + } else if #[cfg(feature = "localnet")] { + /// Core Bridge program ID on Wormhole's Tilt (dev) network. + pub const CORE_BRIDGE_PID: Pubkey = pubkey!("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); + pub const CORE_BRIDGE_FEE_COLLECTOR: Pubkey = pubkey!("GXBsgBD3LDn3vkRZF6TfY5RqgajVZ4W5bMAdiAaaUARs"); + pub const CORE_BRIDGE_CONFIG: Pubkey = pubkey!("FKoMTctsC7vJbEqyRiiPskPnuQx2tX1kurmvWByq5uZP"); + + /// Token Bridge program ID on Wormhole's Tilt (dev) network. + pub const TOKEN_BRIDGE_PID: Pubkey = pubkey!("B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE"); + pub const TOKEN_BRIDGE_EMITTER_AUTHORITY: Pubkey = pubkey!("ENG1wQ7CQKH8ibAJ1hSLmJgL9Ucg6DRDbj752ZAfidLA"); + pub const TOKEN_BRIDGE_CUSTODY_AUTHORITY: Pubkey = pubkey!("JCQ1JdJ3vgnvurNAqMvpwaiSwJXaoMFJN53F6sRKejxQ"); + pub const TOKEN_BRIDGE_MINT_AUTHORITY: Pubkey = pubkey!("8P2wAnHr2t4pAVEyJftzz7k6wuCE7aP1VugNwehzCJJY"); + pub const TOKEN_BRIDGE_TRANSFER_AUTHORITY: Pubkey = pubkey!("C1AVBd8PpfHGe1zW42XXVbHsAQf6q5khiRKuGPLbwHkh"); + + /// USDC mint address found on Solana devnet. + /// + /// NOTE: We expect an integrator to load this account by pulling it from Solana devnet. + pub const USDC_MINT: Pubkey = pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + } +} pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); @@ -51,8 +102,6 @@ pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111 #[allow(dead_code)] pub const GUARDIAN_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; #[allow(dead_code)] -pub const USDC_MINT_ADDRESS: Pubkey = solana_program::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); -#[allow(dead_code)] pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // Chain to cctp domain mapping diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/utils/initialize.rs index 4889befc2..24f720210 100644 --- a/solana/programs/matching-engine/tests/utils/initialize.rs +++ b/solana/programs/matching-engine/tests/utils/initialize.rs @@ -21,10 +21,25 @@ use matching_engine::{ }; use super::super::TestingContext; +pub struct InitializeAddresses { + pub custodian_address: Pubkey, + pub auction_config_address: Pubkey, +} + pub struct InitializeFixture { pub test_context: Rc>, pub custodian: Custodian, - pub custodian_address: Pubkey, + pub addresses: InitializeAddresses, +} + +impl InitializeFixture { + pub fn get_custodian_address(&self) -> Pubkey { + self.addresses.custodian_address.clone() + } + + pub fn get_auction_config_address(&self) -> Pubkey { + self.addresses.auction_config_address.clone() + } } #[derive(Debug, PartialEq, Eq)] @@ -132,7 +147,6 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu accounts: accounts.to_account_metas(None), data: ix_data.data(), }; - // Create and sign transaction let mut transaction = Transaction::new_with_payer( &[instruction], @@ -151,6 +165,9 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu .unwrap(); let custodian_data = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); - - InitializeFixture { test_context, custodian: custodian_data, custodian_address: custodian } + let initialize_addresses = InitializeAddresses { + custodian_address: custodian, + auction_config_address: auction_config, + }; + InitializeFixture { test_context, custodian: custodian_data, addresses: initialize_addresses } } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index 84fa2f984..d4cd27855 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -13,4 +13,5 @@ pub mod initialize; pub mod router; pub mod vaa; pub mod auction; -pub use constants::*; \ No newline at end of file +pub mod setup; +pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index f34a52b1f..5766fe541 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -81,16 +81,47 @@ impl TestEndpointInfo { } } +pub struct TestRouterEndpoints { + pub arbitrum: TestRouterEndpoint, + pub ethereum: TestRouterEndpoint, + pub solana: TestRouterEndpoint, +} + +impl TestRouterEndpoints { + pub fn new(arbitrum: TestRouterEndpoint, ethereum: TestRouterEndpoint, solana: TestRouterEndpoint) -> Self { + Self { arbitrum, ethereum, solana } + } + + pub fn get_endpoint_info(&self, chain: Chain) -> TestEndpointInfo { + match chain { + Chain::Arbitrum => self.arbitrum.info.clone(), + Chain::Ethereum => self.ethereum.info.clone(), + Chain::Solana => self.solana.info.clone(), + _ => panic!("Unsupported chain"), + } + } + + pub fn get_endpoint_address(&self, chain: Chain) -> Pubkey { + match chain { + Chain::Arbitrum => self.arbitrum.endpoint_address, + Chain::Ethereum => self.ethereum.endpoint_address, + Chain::Solana => self.solana.endpoint_address, + _ => panic!("Unsupported chain"), + } + } +} + /// A struct representing a router endpoint for testing purposes #[derive(Debug, Clone, Eq, PartialEq)] pub struct TestRouterEndpoint { + pub endpoint_address: Pubkey, pub bump: u8, pub info: TestEndpointInfo, } -impl From<&RouterEndpoint> for TestRouterEndpoint { - fn from(router_endpoint: &RouterEndpoint) -> Self { - Self { bump: router_endpoint.bump, info: (&router_endpoint.info).into() } +impl From<(&RouterEndpoint, Pubkey)> for TestRouterEndpoint { + fn from((router_endpoint, endpoint_address): (&RouterEndpoint, Pubkey)) -> Self { + Self { endpoint_address, bump: router_endpoint.bump, info: (&router_endpoint.info).into() } } } @@ -101,6 +132,11 @@ impl TestRouterEndpoint { } } +pub fn get_router_endpoint_address(program_id: Pubkey, encoded_chain: &[u8; 2]) -> Pubkey { + let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, encoded_chain], &program_id); + router_endpoint_address +} + pub async fn add_cctp_router_endpoint_ix( test_context: &Rc>, admin_owner_or_assistant: Pubkey, @@ -114,10 +150,8 @@ pub async fn add_cctp_router_endpoint_ix( let admin = generate_admin(admin_owner_or_assistant, admin_custodian); let usdc = matching_engine::accounts::Usdc{mint: usdc_mint_address}; - // This should be equivalent to writeUint16BigEndian let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); - let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); - + let router_endpoint_address = get_router_endpoint_address(program_id, &encoded_chain); let local_custody_token_address = Pubkey::find_program_address(&[LOCAL_CUSTODY_TOKEN_SEED_PREFIX, &encoded_chain], &program_id).0; @@ -165,7 +199,7 @@ pub async fn add_cctp_router_endpoint_ix( let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); - let test_router_endpoint = TestRouterEndpoint::from(&endpoint_data); + let test_router_endpoint = TestRouterEndpoint::from((&endpoint_data, router_endpoint_address)); test_router_endpoint.verify_endpoint_info(chain, &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { domain: CHAIN_TO_DOMAIN[chain as usize].1 }); test_router_endpoint } @@ -226,7 +260,77 @@ pub async fn add_local_router_endpoint_ix( let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); - let test_router_endpoint = TestRouterEndpoint::from(&endpoint_data); + let test_router_endpoint = TestRouterEndpoint::from((&endpoint_data, router_endpoint_address)); test_router_endpoint.verify_endpoint_info(chain, &token_router_emitter, Some(&token_router_mint_recipient), matching_engine::state::MessageProtocol::Local { program_id: token_router_program }); test_router_endpoint +} + +pub async fn create_cctp_router_endpoints_test( + test_context: &Rc>, + admin_owner_or_assistant: Pubkey, + custodian_address: Pubkey, + arb_remote_token_messenger: Pubkey, + eth_remote_token_messenger: Pubkey, + usdc_mint_address: Pubkey, + admin_keypair: Rc, + program_id: Pubkey, +) -> [TestRouterEndpoint; 2] { + let arb_chain = Chain::Arbitrum; + let arbitrum_token_router_endpoint = add_cctp_router_endpoint_ix( + test_context, + admin_owner_or_assistant, + custodian_address, + admin_keypair.as_ref(), + program_id, + arb_remote_token_messenger, + usdc_mint_address, + arb_chain, + ).await; + + let eth_chain = Chain::Ethereum; + let ethereum_token_router_endpoint = add_cctp_router_endpoint_ix( + test_context, + admin_owner_or_assistant, + custodian_address, + admin_keypair.as_ref(), + program_id, + eth_remote_token_messenger, + usdc_mint_address, + eth_chain, + ).await; + + [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] +} + +pub async fn create_all_router_endpoints_test( + test_context: &Rc>, + admin_owner_or_assistant: Pubkey, + custodian_address: Pubkey, + arb_remote_token_messenger: Pubkey, + eth_remote_token_messenger: Pubkey, + usdc_mint_address: Pubkey, + admin_keypair: Rc, + program_id: Pubkey, +) -> TestRouterEndpoints { + let [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] = create_cctp_router_endpoints_test( + test_context, + admin_owner_or_assistant.clone(), + custodian_address.clone(), + arb_remote_token_messenger, + eth_remote_token_messenger, + usdc_mint_address, + admin_keypair.clone(), + program_id, + ).await; + + let local_token_router_endpoint = add_local_router_endpoint_ix( + test_context, + admin_owner_or_assistant, + custodian_address, + admin_keypair.as_ref(), + program_id, + &usdc_mint_address, + ).await; + + TestRouterEndpoints::new(arbitrum_token_router_endpoint, ethereum_token_router_endpoint, local_token_router_endpoint) } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs new file mode 100644 index 000000000..d7c8835d5 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -0,0 +1,221 @@ +use solana_program_test::{ProgramTest, ProgramTestContext}; +use solana_sdk::{ + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, +}; +use std::rc::Rc; +use std::cell::RefCell; +use anchor_spl::token::spl_token::{self, instruction::approve}; +use super::{airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}}; +use super::mint::MintFixture; +use super::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router}; +use super::airdrop::airdrop; +use super::account_fixtures::FixtureAccounts; + +pub struct PreTestingContext { + pub program_test: ProgramTest, + pub testing_actors: TestingActors, + pub program_data_pubkey: Pubkey, + pub account_fixtures: FixtureAccounts, +} + +impl PreTestingContext { + /// Setup the pre-test context + /// + /// # Returns + /// + /// A PreTestingContext struct containing the program data account, testing actors, test context, and fixture accounts + pub fn new(program_id: Pubkey, owner_keypair_path: &str) -> Self { + let mut program_test = ProgramTest::new( + "matching_engine", // Replace with your program name + program_id, + None, + ); + program_test.set_compute_max_units(1000000000); + program_test.set_transaction_account_lock_limit(1000); + + // Setup Testing Actors + let testing_actors = TestingActors::new(owner_keypair_path); + + // Initialise Upgrade Manager + let program_data_pubkey = initialise_upgrade_manager(&mut program_test, &program_id, testing_actors.owner.pubkey()); + + // Initialise CCTP Token Messenger Minter + initialise_cctp_token_messenger_minter(&mut program_test); + + // Initialise Wormhole Core Bridge + initialise_wormhole_core_bridge(&mut program_test); + + // Initialise CCTP Message Transmitter + initialise_cctp_message_transmitter(&mut program_test); + + // Initialise Local Token Router + initialise_local_token_router(&mut program_test); + + // Initialise Account Fixtures + let account_fixtures = FixtureAccounts::new(&mut program_test); + + // Add lookup table accounts + FixtureAccounts::add_lookup_table_hack(&mut program_test); + + PreTestingContext { program_test, testing_actors, program_data_pubkey, account_fixtures } + } +} + + +pub struct TestingContext { + pub program_data_account: Pubkey, // Move this into something smarter + pub testing_actors: TestingActors, + pub test_context: Rc>, + pub fixture_accounts: Option, +} + +impl TestingContext { + pub async fn new(mut pre_testing_context: PreTestingContext, usdc_mint_fixture_path: &str, usdc_mint_address: Pubkey) -> Self { + let test_context = Rc::new(RefCell::new(pre_testing_context.program_test.start_with_context().await)); + + // Airdrop to all actors + pre_testing_context.testing_actors.airdrop_all(&test_context).await; + + // Create USDC mint + let _mint_fixture = MintFixture::new_from_file(&test_context, usdc_mint_fixture_path); + + // Create USDC ATAs for all actors that need them + pre_testing_context.testing_actors.create_atas(&test_context, usdc_mint_address).await; + + TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, test_context, fixture_accounts: Some(pre_testing_context.account_fixtures) } + } +} + +#[derive(Clone)] +pub struct Solver { + pub actor: TestingActor, +} + +impl Solver { + pub fn new(keypair: Rc, token_account: Option) -> Self { + Self { actor: TestingActor::new(keypair, token_account) } + } + + pub fn keypair(&self) -> Rc { + self.actor.keypair.clone() + } + + pub fn pubkey(&self) -> Pubkey { + self.actor.keypair.pubkey() + } + + pub fn token_account_address(&self) -> Option { + self.actor.token_account.as_ref().map(|t| t.address) + } + + pub async fn approve_usdc(&self, test_context: &Rc>, delegate: &Pubkey, amount: u64) { + // If signer pubkeys are empty, it means that the owner is the signer + let approve_ix = approve(&spl_token::ID, &self.token_account_address().unwrap(), delegate, &self.actor.pubkey(), &[], amount).expect("Failed to approve USDC"); + let transaction = Transaction::new_signed_with_payer( + &[approve_ix], + Some(&self.actor.pubkey()), + &[&self.actor.keypair()], + test_context.borrow().last_blockhash, + ); + test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); + } +} + +#[derive(Clone)] +pub struct TestingActor { + pub keypair: Rc, + pub token_account: Option, +} + +impl std::fmt::Debug for TestingActor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", self.keypair.pubkey(), self.token_account) + } +} + +impl TestingActor { + pub fn new(keypair: Rc, token_account: Option) -> Self { + Self { keypair, token_account } + } + pub fn pubkey(&self) -> Pubkey { + self.keypair.pubkey() + } + pub fn keypair(&self) -> Rc { + self.keypair.clone() + } + + pub fn token_account_address(&self) -> Option { + self.token_account.as_ref().map(|t| t.address) + } +} + +/// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) +pub struct TestingActors { + pub owner: TestingActor, + pub owner_assistant: TestingActor, + pub fee_recipient: TestingActor, + pub relayer: TestingActor, + pub solvers: Vec, + pub liquidator: TestingActor, +} + +impl TestingActors { + pub fn new(owner_keypair_path: &str) -> Self { + let owner_kp = Rc::new(read_keypair_from_file(owner_keypair_path)); + let owner = TestingActor::new(owner_kp.clone(), None); + let owner_assistant = TestingActor::new(owner_kp.clone(), None); + let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None); + let relayer = TestingActor::new(Rc::new(Keypair::new()), None); + // TODO: Change player 1 solver to use the keyfile + let mut solvers = vec![]; + solvers.extend(vec![ + Solver::new(Rc::new(Keypair::new()), None), + Solver::new(Rc::new(Keypair::new()), None), + Solver::new(Rc::new(Keypair::new()), None), + ]); + let liquidator = TestingActor::new(Rc::new(Keypair::new()), None); + Self { owner, owner_assistant, fee_recipient, relayer, solvers, liquidator } + } + + pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { + let mut actors = Vec::new(); + actors.push(&mut self.fee_recipient); + for solver in &mut self.solvers { + actors.push(&mut solver.actor); + } + actors.push(&mut self.liquidator); + actors + } + + /// Transfer Lamports to Executors + async fn airdrop_all(&self, test_context: &Rc>) { + airdrop(test_context, &self.owner.pubkey(), 10000000000).await; + airdrop(test_context, &self.owner_assistant.pubkey(), 10000000000).await; + airdrop(test_context, &self.fee_recipient.pubkey(), 10000000000).await; + airdrop(test_context, &self.relayer.pubkey(), 10000000000).await; + for solver in self.solvers.iter() { + airdrop(test_context, &solver.pubkey(), 10000000000).await; + } + airdrop(test_context, &self.liquidator.pubkey(), 10000000000).await; + } + + /// Set up ATAs for Various Owners + async fn create_atas(&mut self, test_context: &Rc>, usdc_mint_address: Pubkey) { + for actor in self.token_account_actors() { + let usdc_ata = create_token_account(test_context.clone(), &actor.keypair(), &usdc_mint_address).await; + airdrop_usdc(test_context, &usdc_ata.address, &actor.pubkey(), 420_000__000_000).await; + actor.token_account = Some(usdc_ata); + } + } + + /// Add solvers to the testing actors + #[allow(dead_code)] + async fn add_solvers(&mut self, test_context: &Rc>, num_solvers: usize, usdc_mint_address: Pubkey) { + for _ in 0..num_solvers { + let keypair = Rc::new(Keypair::new()); + let usdc_ata = create_token_account(test_context.clone(), &keypair, &usdc_mint_address).await; + airdrop(test_context, &keypair.pubkey(), 10000000000).await; + self.solvers.push(Solver::new(keypair.clone(), Some(usdc_ata))); + } + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/programs/matching-engine/tests/utils/token_account.rs index 009dfb858..2c5b1c065 100644 --- a/solana/programs/matching-engine/tests/utils/token_account.rs +++ b/solana/programs/matching-engine/tests/utils/token_account.rs @@ -4,7 +4,7 @@ use anchor_spl::associated_token::spl_associated_token_account; use solana_program_test::ProgramTestContext; use std::{cell::RefCell, fs, rc::Rc}; - +#[derive(Clone)] pub struct TokenAccountFixture { pub test_ctx: Rc>, pub address: Pubkey, diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index f849335b2..26304a067 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -4,7 +4,6 @@ use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; use common::wormhole_cctp_solana::wormhole::VaaAccount; // TODO: Remove this if not needed use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; // TODO: Remove this if not needed -use matching_engine::instruction::PlaceInitialOfferCctp; use super::constants::Chain; use super::CHAIN_TO_DOMAIN; @@ -31,7 +30,7 @@ pub trait DataDiscriminator { #[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct PostedVaaData { /// Header of the posted VAA - pub vaa_version: u8, + // pub vaa_version: u8, (This is removed because it is encoded in the discriminator) /// Level of consistency requested by the emitter pub consistency_level: u8, @@ -75,7 +74,6 @@ impl PostedVaaData { .as_secs() as u32; let emitter_chain = chain.to_chain_id(); Self { - vaa_version: 1, consistency_level: 1, vaa_time: timestamp, vaa_signature_account: Pubkey::new_unique(), @@ -100,6 +98,10 @@ impl PostedVaaData { ]) } + pub fn digest(&self) -> [u8; 32] { + keccak::hashv(&[self.message_hash().as_ref()]).as_ref().try_into().unwrap() + } + pub fn create_vaa_account(&self, program_test: &mut ProgramTest) -> Pubkey { let vaa_hash = self.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); @@ -143,6 +145,25 @@ where Ok(data) } +#[derive(Clone)] +pub struct TestVaa { + pub kind: TestVaaKind, + pub vaa_pubkey: Pubkey, + pub vaa_data: PostedVaaData, +} + +impl TestVaa { + pub fn get_vaa_pubkey(&self) -> Pubkey { + self.vaa_pubkey.clone() + } +} + +#[derive(Clone)] +pub enum TestVaaKind { + Deposit, + FastTransfer, +} + pub struct TestFastTransfer { pub token_mint: Pubkey, pub source_address: ChainAddress, @@ -150,31 +171,29 @@ pub struct TestFastTransfer { pub destination_address: ChainAddress, pub cctp_nonce: u32, pub sequence: u64, - pub deposit_vaa_pubkey: Pubkey, - pub fast_transfer_vaa_pubkey: Pubkey, - pub deposit_vaa_data: PostedVaaData, - pub fast_transfer_vaa_data: PostedVaaData, + pub fast_transfer_vaa: TestVaa, // kind: TestVaaKind::FastTransfer + pub deposit_vaa: TestVaa, // kind: TestVaaKind::Deposit } impl TestFastTransfer { pub fn new(program_test: &mut ProgramTest, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> Self { let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(program_test, token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient); let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(program_test, start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence); - Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa_pubkey, fast_transfer_vaa_pubkey, deposit_vaa_data, fast_transfer_vaa_data } + Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data } } } pub async fn verify_vaas(&self, test_context: &Rc>) { - let expected_deposit_vaa = self.deposit_vaa_data.clone(); - let expected_fast_transfer_vaa = self.fast_transfer_vaa_data.clone(); + let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); + let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); { - let deposit_vaa = test_context.borrow_mut().banks_client.get_account(self.deposit_vaa_pubkey.clone()).await.unwrap(); + let deposit_vaa = test_context.borrow_mut().banks_client.get_account(self.deposit_vaa.vaa_pubkey.clone()).await.unwrap(); assert!(deposit_vaa.is_some(), "Deposit VAA not found"); let deposit_vaa = deserialize_with_discriminator::(&deposit_vaa.unwrap().data).unwrap(); assert_eq!(deposit_vaa, expected_deposit_vaa); } { - let fast_transfer_vaa = test_context.borrow_mut().banks_client.get_account(self.fast_transfer_vaa_pubkey.clone()).await.unwrap(); + let fast_transfer_vaa = test_context.borrow_mut().banks_client.get_account(self.fast_transfer_vaa.vaa_pubkey.clone()).await.unwrap(); assert!(fast_transfer_vaa.is_some(), "Fast transfer VAA not found"); let fast_transfer_vaa = deserialize_with_discriminator::(&fast_transfer_vaa.unwrap().data).unwrap(); assert_eq!(fast_transfer_vaa, expected_fast_transfer_vaa); @@ -210,13 +229,13 @@ pub fn create_fast_transfer_message(program_test: &mut ProgramTest, start_timest let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); // Implements TypePrefixedPayload let fast_market_order = FastMarketOrder { - amount_in: 100, - min_amount_out: 100, + amount_in: 1000, + min_amount_out: 1000, target_chain: destination_address.chain.to_chain_id(), redeemer: destination_address.address.to_bytes(), sender: source_address.address.to_bytes(), refund_address: refund_address.address.to_bytes(), // Not used so can be all zeros - max_fee: 100, // USDC max fee + max_fee: 1000000000, // USDC max fee init_auction_fee: 10, // USDC init auction fee (the first person to verify a vaa and start an auction will get this fee) so at least rent deadline, // If dealine is 0 then there is no deadline redeemer_message: WriteableBytes::new(vec![]), @@ -255,13 +274,21 @@ impl TestFastTransfers { pub fn create_vaas_test(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey) -> TestFastTransfers { let mut test_fast_transfers = TestFastTransfers::new(); - let source_address = ChainAddress::new_unique(Chain::Solana); + let source_address = ChainAddress::new_unique(Chain::Arbitrum); let destination_address = ChainAddress::new_unique(Chain::Ethereum); let refund_address = source_address.clone(); test_fast_transfers.add_ft(program_test, start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); test_fast_transfers } +pub fn create_vaas_test_with_chain_and_address(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32]) -> TestFastTransfers { + let mut test_fast_transfers = TestFastTransfers::new(); + let source_address = ChainAddress::new_with_address(source_chain, source_address); + let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); + let refund_address = source_address.clone(); + test_fast_transfers.add_ft(program_test, start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); + test_fast_transfers +} pub trait ToBytes { fn to_bytes(&self) ->[u8; 32]; } @@ -270,6 +297,7 @@ pub trait ToBytes { pub enum TestPubkey { Solana(Pubkey), Evm(EvmAddress), + Bytes([u8; 32]), } impl ToBytes for TestPubkey { @@ -277,6 +305,7 @@ impl ToBytes for TestPubkey { match self { TestPubkey::Solana(pubkey) => pubkey.to_bytes(), TestPubkey::Evm(evm_address) => evm_address.to_bytes(), + TestPubkey::Bytes(bytes) => *bytes, } } } @@ -348,5 +377,9 @@ impl ChainAddress { Chain::Base => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, } } + + pub fn new_with_address(chain: Chain, address: [u8; 32]) -> Self { + Self { chain, address: TestPubkey::Bytes(address) } + } } From 296a10c1b829345522b8758bbfb85fd2fac38253 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 14 Feb 2025 18:05:38 +0000 Subject: [PATCH 007/112] saved cargo.lock changes --- solana/Cargo.lock | 283 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 223 insertions(+), 60 deletions(-) diff --git a/solana/Cargo.lock b/solana/Cargo.lock index e0a31a5ac..e5132bfcc 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -579,7 +579,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -632,6 +632,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -647,6 +653,22 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -776,7 +798,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "syn_derive", ] @@ -893,7 +915,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1250,7 +1272,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1261,7 +1283,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1380,6 +1402,27 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1388,7 +1431,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1411,7 +1454,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1511,7 +1554,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1524,7 +1567,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1682,7 +1725,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -1803,7 +1846,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util 0.7.13", @@ -1852,6 +1895,12 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heck" version = "0.3.3" @@ -1888,6 +1937,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -2146,7 +2204,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -2229,12 +2287,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.15.2", ] [[package]] @@ -2478,13 +2536,19 @@ version = "0.0.0" dependencies = [ "anchor-lang", "anchor-spl", + "base64 0.22.1", "bincode", + "bs58 0.5.0", "cfg-if", "hex", "hex-literal", + "lazy_static", "liquidity-layer-common-solana", "ruint", + "secp256k1", + "serde", "serde_json", + "solana-cli-output", "solana-program", "solana-program-test", "solana-sdk", @@ -2716,7 +2780,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -2798,7 +2862,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -2810,7 +2874,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -2991,7 +3055,7 @@ checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -3089,6 +3153,12 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -3143,9 +3213,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3167,7 +3237,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -3357,6 +3427,17 @@ dependencies = [ "bitflags 2.4.2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.11", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -3635,7 +3716,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -3648,6 +3729,26 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.10.0" @@ -3688,9 +3789,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -3706,13 +3807,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -3767,7 +3868,20 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.7.1", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", ] [[package]] @@ -4113,6 +4227,49 @@ dependencies = [ "url", ] +[[package]] +name = "solana-cli-config" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5ded97f71d1ff4de9b256fc33acab9f9def864d5aa16762c8f91b67c66466c" +dependencies = [ + "dirs-next", + "lazy_static", + "serde", + "serde_derive", + "serde_yaml", + "solana-clap-utils", + "solana-sdk", + "url", +] + +[[package]] +name = "solana-cli-output" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6b601aa9f9764afc60ba310c42ff8576923fcf3dd5da30fd0cdf9c44caf98" +dependencies = [ + "Inflector", + "base64 0.21.7", + "chrono", + "clap 2.34.0", + "console", + "humantime", + "indicatif", + "pretty-hex", + "semver", + "serde", + "serde_json", + "solana-account-decoder", + "solana-clap-utils", + "solana-cli-config", + "solana-rpc-client-api", + "solana-sdk", + "solana-transaction-status", + "solana-vote-program", + "spl-memo", +] + [[package]] name = "solana-client" version = "1.18.26" @@ -4124,7 +4281,7 @@ dependencies = [ "dashmap", "futures", "futures-util", - "indexmap 2.1.0", + "indexmap 2.7.1", "indicatif", "log", "quinn", @@ -4180,7 +4337,7 @@ dependencies = [ "bincode", "crossbeam-channel", "futures-util", - "indexmap 2.1.0", + "indexmap 2.7.1", "log", "rand 0.8.5", "rayon", @@ -4250,7 +4407,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -4756,7 +4913,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -4807,7 +4964,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "histogram", - "indexmap 2.1.0", + "indexmap 2.7.1", "itertools", "libc", "log", @@ -4867,7 +5024,7 @@ dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 2.1.0", + "indexmap 2.7.1", "indicatif", "log", "rayon", @@ -5125,7 +5282,7 @@ checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn 0.1.2", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5136,7 +5293,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn 0.2.0", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5148,7 +5305,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.58", + "syn 2.0.98", "thiserror", ] @@ -5161,7 +5318,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.58", + "syn 2.0.98", "thiserror", ] @@ -5235,7 +5392,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5247,7 +5404,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5524,9 +5681,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -5542,7 +5699,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5571,7 +5728,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5688,7 +5845,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5699,7 +5856,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "test-case-core", ] @@ -5735,7 +5892,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5874,7 +6031,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -5993,7 +6150,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.7.1", "toml_datetime", "winnow 0.5.33", ] @@ -6004,7 +6161,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.7.1", "toml_datetime", "winnow 0.5.33", ] @@ -6015,7 +6172,7 @@ version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -6048,7 +6205,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -6176,6 +6333,12 @@ dependencies = [ "void", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.7.1" @@ -6327,7 +6490,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -6361,7 +6524,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6765,7 +6928,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "synstructure 0.13.1", ] @@ -6786,7 +6949,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -6806,7 +6969,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", "synstructure 0.13.1", ] @@ -6827,7 +6990,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] @@ -6849,7 +7012,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.98", ] [[package]] From 5c5eb3bbd058c4cc79a068a397739315cfb36854 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 20 Feb 2025 16:07:30 -0800 Subject: [PATCH 008/112] [broken] saving work --- .gitmodules | 4 + solana/lib/wormhole | 1 + solana/programs/matching-engine/Cargo.toml | 7 + .../matching-engine/src/composite/mod.rs | 14 + solana/programs/matching-engine/src/error.rs | 2 + solana/programs/matching-engine/src/lib.rs | 13 +- .../auction/offer/place_initial/cctp.rs | 2 +- .../auction/offer/place_initial/cctp_shim.rs | 355 ++++++++++++++++++ .../auction/offer/place_initial/mod.rs | 2 + .../fixtures/wormhole_post_message_shim.so | Bin 0 -> 40320 bytes .../fixtures/wormhole_verify_vaa_shim.so | Bin 0 -> 97208 bytes .../tests/initialize_integration_tests.rs | 85 ++++- .../matching-engine/tests/utils/constants.rs | 4 + .../matching-engine/tests/utils/mod.rs | 1 + .../tests/utils/program_fixtures.rs | 35 +- .../matching-engine/tests/utils/setup.rs | 10 +- .../matching-engine/tests/utils/shims.rs | 340 +++++++++++++++++ .../matching-engine/tests/utils/vaa.rs | 8 +- 18 files changed, 866 insertions(+), 17 deletions(-) create mode 160000 solana/lib/wormhole create mode 100644 solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs create mode 100755 solana/programs/matching-engine/tests/fixtures/wormhole_post_message_shim.so create mode 100755 solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so create mode 100644 solana/programs/matching-engine/tests/utils/shims.rs diff --git a/.gitmodules b/.gitmodules index ca439845a..5c3926225 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,7 @@ path = evm/lib/wormhole-solidity-sdk url = https://github.com/wormhole-foundation/wormhole-solidity-sdk branch = 2b7db51f99b49eda99b44f4a044e751cb0b2e8ea +[submodule "solana/lib/wormhole"] + path = solana/lib/wormhole + url = https://github.com/wormholelabs-xyz/wormhole.git + branch = svm-shims-2 diff --git a/solana/lib/wormhole b/solana/lib/wormhole new file mode 160000 index 000000000..f69b3ae36 --- /dev/null +++ b/solana/lib/wormhole @@ -0,0 +1 @@ +Subproject commit f69b3ae366211276fe15554f83a2d76abee0535c diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index fda8042bc..68ea744f2 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -39,6 +39,11 @@ solana-program.workspace = true hex.workspace = true ruint.workspace = true cfg-if.workspace = true +wormhole-svm-definitions.workspace = true + + +# wormhole-bridge-solana.workspace = true + [dev-dependencies] @@ -53,6 +58,8 @@ lazy_static = "1.4.0" bs58 = "0.5.0" serde = { version = "1.0.212", features = ["derive"] } secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context"] } +wormhole-svm-shim.workspace = true +wormhole-svm-definitions.workspace = true [lints] workspace = true diff --git a/solana/programs/matching-engine/src/composite/mod.rs b/solana/programs/matching-engine/src/composite/mod.rs index d993d87f9..e994d4ed5 100644 --- a/solana/programs/matching-engine/src/composite/mod.rs +++ b/solana/programs/matching-engine/src/composite/mod.rs @@ -18,6 +18,10 @@ use common::{ wormhole::{core_bridge_program, VaaAccount}, }, }; +use wormhole_svm_bridge::{ + GuardianSet, + GuardianSetSignatures, +}; #[derive(Accounts)] pub struct Usdc<'info> { @@ -258,6 +262,16 @@ pub struct LiveRouterPath<'info> { pub to_endpoint: LiveRouterEndpoint<'info>, } +// TODO: Add a composite called FastOrderPathShim with two accounts: Guardian Set and Guardian Set Signatures +// Call verify hash on the instruction on the verify shim program +#[derive(Accounts)] +pub struct FastOrderPathShim<'info> { + pub guardian_set: UncheckedAccount<'info>, + pub guardian_set_signatures: UncheckedAccount<'info>, + pub live_router_path: LiveRouterPath<'info>, +} + + #[derive(Accounts)] pub struct FastOrderPath<'info> { #[account( diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index b67e4023a..847815f02 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -87,6 +87,8 @@ pub enum MatchingEngineError { CannotCloseAuctionYet = 0x500, AuctionHistoryNotFull = 0x502, AuctionHistoryFull = 0x504, + + InvalidVerifyVaaShimProgram = 0x600, } #[cfg(test)] diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 61b4d3f94..14f36ba93 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -23,17 +23,17 @@ cfg_if::cfg_if! { declare_id!("HtkeCDdYY4i9ncAxXKjYTx8Uu3WM8JbtiLRYjtHwaVXb"); const CUSTODIAN_BUMP: u8 = 254; - const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); + pub const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); } else if #[cfg(feature = "testnet")] { declare_id!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); const CUSTODIAN_BUMP: u8 = 254; - const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); + pub const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); } else if #[cfg(feature = "localnet")] { declare_id!("MatchingEngine11111111111111111111111111111"); const CUSTODIAN_BUMP: u8 = 254; - const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); + pub const CCTP_MINT_RECIPIENT: Pubkey = pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); } } @@ -254,6 +254,13 @@ pub mod matching_engine { processor::place_initial_offer_cctp(ctx, offer_price) } + /// This instruction is used to create a new auction given a valid `VaaShim`. + /// This instruction should act in the exact same way as `place_initial_offer_cctp` except that + /// it will check the digest of the vaa directly using a cpi call to the verify shim program. + pub fn place_initial_offer_cctp_shim(ctx: Context, offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage) -> Result<()> { + processor::place_initial_offer_cctp_shim(ctx, offer_price, guardian_set_bump, vaa_message) + } + /// This instruction is used to improve an existing auction offer. The `offer_price` must be /// greater than the current `offer_price` in the auction. This instruction will revert if the /// `offer_price` is less than the current `offer_price`. This instruction can be called by diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs index 44f301b0f..e9832bcf2 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs @@ -205,4 +205,4 @@ pub fn place_initial_offer_cctp( .checked_add(security_deposit) .ok_or_else(|| MatchingEngineError::U64Overflow)?, ) -} +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs new file mode 100644 index 000000000..7b83f0b0f --- /dev/null +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -0,0 +1,355 @@ +use crate::{ + composite::*, + error::MatchingEngineError, + state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, MessageProtocol}, + utils, +}; +use anchor_lang::prelude::*; +use anchor_spl::token; +use common::{messages::{raw::LiquidityLayerMessage, FastMarketOrder}, TRANSFER_AUTHORITY_SEED_PREFIX}; +use solana_sdk::program::invoke_signed_unchecked; +use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashData}; +use common::wormhole_io::TypePrefixedPayload; +use solana_program::{keccak, instruction::Instruction, program::invoke_signed}; + + +#[derive(Accounts)] +#[instruction(offer_price: u64)] +#[event_cpi] +pub struct PlaceInitialOfferCctpShim<'info> { + #[account(mut)] + payer: Signer<'info>, + + /// The auction participant needs to set approval to this PDA. + /// + /// CHECK: Seeds must be \["transfer-authority", auction.key(), offer_price.to_be_bytes()\]. + #[account( + seeds = [ + TRANSFER_AUTHORITY_SEED_PREFIX, + auction.key().as_ref(), + &offer_price.to_be_bytes() + ], + bump + )] + transfer_authority: UncheckedAccount<'info>, + + /// NOTE: This account is only used to pause inbound auctions. + #[account(constraint = !custodian.paused @ MatchingEngineError::Paused)] + custodian: CheckedCustodian<'info>, + + #[account( + constraint = { + require_eq!( + auction_config.id, + custodian.auction_config_id, + MatchingEngineError::AuctionConfigMismatch, + ); + + true + } + )] + auction_config: Account<'info, AuctionConfig>, + + /// The cpi instruction will verify the hash of the fast order path so no account constraints are needed. + fast_order_path_shim: FastOrderPathShim<'info>, + + /// This account should only be created once, and should never be changed to + /// init_if_needed. Otherwise someone can game an existing auction. + #[account( + init, + payer = payer, + space = 8 + Auction::INIT_SPACE, + seeds = [ + Auction::SEED_PREFIX, + fast_order_path.fast_vaa.load_unchecked().digest().as_ref(), + ], + bump + )] + auction: Box>, + + #[account(mut)] + offer_token: Box>, + + #[account( + init, + payer = payer, + token::mint = usdc, + token::authority = auction, + seeds = [ + crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction.key().as_ref(), + ], + bump, + )] + auction_custody_token: Box>, + + usdc: Usdc<'info>, + + #[account(constraint = { + require_eq!( + verify_vaa_shim_program.key(), + wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + MatchingEngineError::InvalidVerifyVaaShimProgram + ); + + true + })] + verify_vaa_shim_program: UncheckedAccount<'info>, + system_program: Program<'info, System>, + token_program: Program<'info, token::Token>, +} + +/// A vaa message is the serialised message body of a posted vaa. Only the fields that are required to create the digest are included. +pub struct VaaMessage(Vec); + +impl VaaMessage { + pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32], payload: Vec) -> Self { + Self(VaaMessageBody::new(consistency_level, vaa_time, sequence, emitter_chain, emitter_address, payload).to_vec()) + } + + fn message_hash(&self) -> keccak::Hash { + keccak::hashv(&[self.0.as_ref()]) + } + + pub fn digest(&self) -> [u8; 32] { + self.message_hash().as_ref().try_into().unwrap() + } + + fn payload(&self) -> Vec { + // Calculate offset: + // vaa_time (u32) = 4 bytes + // nonce (u32) = 4 bytes + // emitter_chain (u16) = 2 bytes + // emitter_address = 32 bytes + // sequence (u64) = 8 bytes + // consistency_level (u8) = 1 byte + // Total offset = 51 bytes + + // Everything after the offset is the payload + self.0[51..].to_vec() + } + + fn vaa_time(&self) -> u32 { + // vaa_time is the first 4 bytes of the message + u32::from_be_bytes(self.0[0..4].try_into().unwrap()) + } + + fn nonce(&self) -> u32 { + // nonce is the next 4 bytes of the message + u32::from_be_bytes(self.0[4..8].try_into().unwrap()) + } + + fn emitter_chain(&self) -> u16 { + // emitter_chain is the next 2 bytes of the message + u16::from_be_bytes(self.0[8..10].try_into().unwrap()) + } + + fn emitter_address(&self) -> [u8; 32] { + // emitter_address is the next 32 bytes of the message + self.0[10..42].try_into().unwrap() + } + + fn sequence(&self) -> u64 { + // sequence is the next 8 bytes of the message + u64::from_be_bytes(self.0[42..50].try_into().unwrap()) + } + + +} + +pub struct Payload(Vec); + +impl Payload { + pub fn new(amount_in: u64, min_amount_out: u64, target_chain: u16, redeemer: [u8; 32], sender: [u8; 32], refund_address: [u8; 32], max_fee: u64, init_auction_fee: u64, deadline: u32, redeemer_message: Vec) -> Self { + let fast_market_order = FastMarketOrder { + amount_in, + min_amount_out, + target_chain, + redeemer, + sender, + refund_address, + max_fee, + init_auction_fee, + deadline, + redeemer_message, + }; + Self(fast_market_order.to_vec()) + } +} + +/// Just a helper struct to make the code more readable. +struct VaaMessageBody { + + /// Level of consistency requested by the emitter + pub consistency_level: u8, + + /// Time the vaa was submitted + pub vaa_time: u32, + + /// Account where signatures are stored + pub vaa_signature_account: Pubkey, + + /// Time the posted message was created + pub submission_time: u32, + + /// Unique nonce for this message + pub nonce: u32, + + /// Sequence number of this message + pub sequence: u64, + + /// Emitter of the message + pub emitter_chain: u16, + + /// Emitter of the message + pub emitter_address: [u8; 32], + + /// Message payload + pub payload: Vec, +} + +impl VaaMessageBody { + pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32], payload: Vec) -> Self { + Self { + consistency_level, + vaa_time, + vaa_signature_account: Pubkey::new_unique(), // Doesn't matter for the hash + submission_time: 0, // Doesn't matter for the hash + nonce: 0, // Always 0 + sequence, + emitter_chain, // Can be taken from the live router path + emitter_address, // Can be taken from the live router path + payload, + } + } + + fn to_vec(&self) -> Vec { + vec![ + self.vaa_time.to_be_bytes().as_ref(), + self.nonce.to_be_bytes().as_ref(), + self.emitter_chain.to_be_bytes().as_ref(), + &self.emitter_address, + &self.sequence.to_be_bytes(), + &[self.consistency_level], + self.payload.as_ref(), + ].concat() + } +} + +pub fn place_initial_offer_cctp_shim( + ctx: Context, + offer_price: u64, + guardian_set_bump: u8, + vaa_message: VaaMessage, +) -> Result<()> { + // Extract the guardian set and guardian set signatures accounts from the FastOrderPathShim. + let FastOrderPathShim{guardian_set, guardian_set_signatures, live_router_path} = ctx.accounts.fast_order_path_shim; + + // Check that the VAA message corresponds to the accounts in the FastOrderPathShim. + let from_endpoint = live_router_path.from_endpoint; + assert_eq!(from_endpoint.chain, vaa_message.emitter_chain()); + assert_eq!(from_endpoint.address, vaa_message.emitter_address()); + + let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message.digest()); + + // Call the verify shim program using cpi to verify the hash of the shim. + let verify_shim_ix = Instruction { + program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: vec![ + AccountMeta::new(guardian_set.key(), false), + AccountMeta::new(guardian_set_signatures.key(), false), + ], + data: verify_hash_data.to_vec(), + }; + + // Make the cpi call to verify the shim. + invoke_signed_unchecked(&verify_shim_ix, &[ + guardian_set.to_account_info(), + guardian_set_signatures.to_account_info(), + ], &[&[]])?; + + let order = LiquidityLayerMessage::try_from(vaa_message.payload()) + .unwrap() + .to_fast_market_order_unchecked(); + + // Parse the transfer amount from the VAA. + let amount_in = order.amount_in(); + + // Saturating to u64::MAX is safe here. If the amount really ends up being this large, the + // checked addition below will catch it. + let security_deposit = + order + .max_fee() + .saturating_add(utils::auction::compute_notional_security_deposit( + &ctx.accounts.auction_config, + amount_in, + )); + + // Set up the Auction account for this auction. + let config = &ctx.accounts.auction_config; + let initial_offer_token = ctx.accounts.offer_token.key(); + ctx.accounts.auction.set_inner(Auction { + bump: ctx.bumps.auction, + vaa_hash: vaa_message.digest(), + vaa_timestamp: vaa_message.vaa_time(), + target_protocol: live_router_path.to_endpoint.protocol, + status: AuctionStatus::Active, + prepared_by: ctx.accounts.payer.key(), + info: AuctionInfo { + config_id: config.id, + custody_token_bump: ctx.bumps.auction_custody_token, + vaa_sequence: vaa_message.sequence(), + source_chain: vaa_message.emitter_chain(), + best_offer_token: initial_offer_token, + initial_offer_token, + start_slot: Clock::get().unwrap().slot, + amount_in, + security_deposit, + offer_price, + redeemer_message_len: order.redeemer_message_len(), + destination_asset_info: Default::default(), + } + .into(), + }); + + let info = ctx.accounts.auction.info.as_ref().unwrap(); + + // Emit event for auction participants to listen to. + emit_cpi!(crate::utils::log_emit(crate::events::AuctionUpdated { + config_id: info.config_id, + fast_vaa_hash: ctx.accounts.auction.vaa_hash, + vaa: None, + source_chain: info.source_chain, + target_protocol: ctx.accounts.auction.target_protocol, + redeemer_message_len: info.redeemer_message_len, + end_slot: info.auction_end_slot(config), + best_offer_token: initial_offer_token, + token_balance_before: ctx.accounts.offer_token.amount, + amount_in, + total_deposit: info.total_deposit(), + max_offer_price_allowed: utils::auction::compute_min_allowed_offer(config, info) + .checked_sub(1), + })); + + // Finally transfer tokens from the offer authority's token account to the + // auction's custody account. + token::transfer( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + anchor_spl::token::Transfer { + from: ctx.accounts.offer_token.to_account_info(), + to: ctx.accounts.auction_custody_token.to_account_info(), + authority: ctx.accounts.transfer_authority.to_account_info(), + }, + &[&[ + TRANSFER_AUTHORITY_SEED_PREFIX, + ctx.accounts.auction.key().as_ref(), + &offer_price.to_be_bytes(), + &[ctx.bumps.transfer_authority], + ]], + ), + amount_in + .checked_add(security_deposit) + .ok_or_else(|| MatchingEngineError::U64Overflow)?, + ) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs index c143ed248..baa4bb73e 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs @@ -1,2 +1,4 @@ mod cctp; pub use cctp::*; +mod cctp_shim; +pub use cctp_shim::*; diff --git a/solana/programs/matching-engine/tests/fixtures/wormhole_post_message_shim.so b/solana/programs/matching-engine/tests/fixtures/wormhole_post_message_shim.so new file mode 100755 index 0000000000000000000000000000000000000000..a27941bdd09df230b8cb06852232e1b7de1e8e98 GIT binary patch literal 40320 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 KMbhVZ`~MH5{?o4j literal 0 HcmV?d00001 diff --git a/solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so b/solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so new file mode 100755 index 0000000000000000000000000000000000000000..37df291820197a79c746f8b540710dd2aacf1364 GIT binary patch literal 97208 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 literal 0 HcmV?d00001 diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 5cd40d7ec..53035feac 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,6 +1,7 @@ +use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; - +use secp256k1::SecretKey as SecpSecretKey; mod utils; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test, get_router_endpoint_address}; @@ -9,21 +10,23 @@ use utils::account_fixtures::FixtureAccounts; use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; use utils::setup::{PreTestingContext, TestingContext}; use utils::vaa::{create_vaas_test, create_vaas_test_with_chain_and_address}; +use utils::shims::{set_up_post_message_transaction_test, set_up_verify_shims_test}; +use utils::constants::*; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { - const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"); - const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"); + //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint.json"; } else if #[cfg(feature = "testnet")] { - const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); - const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); + //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; } else if #[cfg(feature = "localnet")] { - const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); - const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); + // const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); } } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; @@ -31,7 +34,6 @@ const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vg /// Test that the program is initialised correctly #[tokio::test] pub async fn test_initialize_program() { - let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; @@ -119,8 +121,6 @@ pub async fn test_setup_vaas() { ).await; let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; - let _local_endpoint_address = router_endpoints.solana.endpoint_address; - // TODO: Get auction pubkey let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( @@ -141,3 +141,68 @@ pub async fn test_setup_vaas() { let improved_offer_fixture = improve_offer(&testing_context.test_context, initial_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } + + +#[tokio::test] +pub async fn test_post_message_shims() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + // Add shim programs + pre_testing_context.add_post_message_shims(); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let actors = testing_context.testing_actors; + let emitter_signer = actors.owner.keypair(); + let payer_signer = actors.solvers[0].keypair(); + let recent_blockhash = testing_context.test_context.borrow().last_blockhash; + set_up_post_message_transaction_test(&testing_context.test_context, &payer_signer, &emitter_signer, recent_blockhash).await; +} + +#[tokio::test] +pub async fn test_verify_shims() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + set_up_verify_shims_test(&testing_context.test_context, &testing_context.testing_actors.owner.keypair()).await; + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let first_test_ft = vaas_test.0.first().unwrap(); + // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim + + // TODO: Load this from a file + let guardian_secret_key = SecpSecretKey::load("cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0").expect("Failed to load guardian secret key"); + let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + // Try making initial offer using the shim instruction + let usdc_mint_address = USDC_MINT_ADDRESS; + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = create_all_router_endpoints_test( + &testing_context.test_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + fixture_accounts.arbitrum_remote_token_messenger, + fixture_accounts.ethereum_remote_token_messenger, + usdc_mint_address, + testing_context.testing_actors.owner.keypair(), + PROGRAM_ID, + ).await; + let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; + let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + + let solver = testing_context.testing_actors.solvers[0].clone(); + let fast_vaa = Pubkey::new_unique(); // This is only needed in order to be compatible with the AuctionAccounts struct + let auction_accounts = AuctionAccounts::new( + fast_vaa, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + arb_endpoint_address, // From router endpoint pubkey + eth_endpoint_address, // To router endpoint pubkey + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + ); + + let fast_market_order = first_test_ft.fast_transfer_vaa.clone(); + + let initial_offer_fixture = place_initial_offer(&testing_context.test_context, &auction_accounts, fast_market_order, testing_context.testing_actors.owner.keypair(), PROGRAM_ID).await; + +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 2b3d3f5b9..e19327145 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -57,6 +57,10 @@ cfg_if::cfg_if! { pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); +pub const WORMHOLE_POST_MESSAGE_SHIM_PID: Pubkey = pubkey!("EtZMZM22ViKMo4r5y4Anovs3wKQ2owUmDpjygnMMcdEX"); +pub const WORMHOLE_VERIFY_VAA_SHIM_PID: Pubkey = pubkey!("EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at"); +pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY: Pubkey = pubkey!("HQS31aApX3DDkuXgSpV9XyDUNtFgQ31pUn5BNWHG2PSp"); +pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY_BUMP: u8 = 255; /// Keypairs as base64 strings (taken from consts.ts in ts tests) // pub const PAYER_KEYPAIR_B64: &str = "cDfpY+VbRFXPPwouZwAx+ha9HqedkhqUr5vUaFa2ucAMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQ=="; diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index d4cd27855..c5078abb2 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -14,4 +14,5 @@ pub mod router; pub mod vaa; pub mod auction; pub mod setup; +pub mod shims; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/program_fixtures.rs b/solana/programs/matching-engine/tests/utils/program_fixtures.rs index 75412d120..d265cb9e1 100644 --- a/solana/programs/matching-engine/tests/utils/program_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/program_fixtures.rs @@ -2,7 +2,7 @@ use solana_program_test::ProgramTest; use solana_sdk::pubkey::Pubkey; use solana_program::bpf_loader_upgradeable; -use super::{TOKEN_ROUTER_PID, CORE_BRIDGE_PID, CCTP_TOKEN_MESSENGER_MINTER_PID, CCTP_MESSAGE_TRANSMITTER_PID}; +use super::{TOKEN_ROUTER_PID, CORE_BRIDGE_PID, CORE_BRIDGE_CONFIG, CCTP_TOKEN_MESSENGER_MINTER_PID, CCTP_MESSAGE_TRANSMITTER_PID, WORMHOLE_POST_MESSAGE_SHIM_PID, WORMHOLE_VERIFY_VAA_SHIM_PID}; fn get_program_data(owner: Pubkey) -> Vec { let state = solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { @@ -58,4 +58,37 @@ pub fn initialise_cctp_message_transmitter(program_test: &mut ProgramTest) { pub fn initialise_local_token_router(program_test: &mut ProgramTest) { let program_id = TOKEN_ROUTER_PID; program_test.add_program("token_router", program_id, None); +} + +pub fn initialise_post_message_shims(program_test: &mut ProgramTest) { + let post_message_program_id = WORMHOLE_POST_MESSAGE_SHIM_PID; + program_test.add_program("wormhole_post_message_shim", post_message_program_id, None); + let verify_vaa_shim_program_id = WORMHOLE_VERIFY_VAA_SHIM_PID; + program_test.add_program("wormhole_verify_vaa_shim", verify_vaa_shim_program_id, None); +} + +pub fn initialise_verify_shims(program_test: &mut ProgramTest) { + let verify_vaa_shim_program_id = WORMHOLE_VERIFY_VAA_SHIM_PID; + program_test.add_program("wormhole_verify_vaa_shim", verify_vaa_shim_program_id, None); + program_test.add_account_with_base64_data( + CORE_BRIDGE_CONFIG, + 1_057_920, + CORE_BRIDGE_PID, + "BAAAAAQYDQ0AAAAAgFEBAGQAAAAAAAAA", + ); + // Guardian set 4 (active). + program_test.add_account_with_base64_data( + find_guardian_set_address(u32::to_be_bytes(4), &CORE_BRIDGE_PID).0, + 3_647_040, + CORE_BRIDGE_PID, + "BAAAABMAAABYk7WnbD9zlkVkiIW9zMBs1wo80/9suVJYm96GLCXvQ5ITL7nUpCFXEU3oRgGTvfOi/PgfhqCXZfR2L9EQegCGsy16CXeSaiBRMdhzHTnL64yCsv2C+u0nEdWa8PJJnRbnJvayEbOXVsBCRBvm2GULabVOvnFeI0NUzltNNI+3S5WOiWbi7D29SVinzRXnyvB8Tj3I58Rp+SyM2I+4AFogdKO/kTlT1pUmDYi8GqJaTu42PvAACsAHZyezX76i2sKP7lzLD+p2jq9FztE2udniSQNGSuiJ9cinI/wU+TEkt8c4hDy7iehkyGLDjN3Mz5XSzDek3ANqjSMrSPYs3UcxQS9IkNp5j2iWozMfZLSMEtHVf9nL5wgRcaob4dNsr+OGeRD5nAnjR4mcGcOBkrbnOHzNdoJ3wX2rG3pQJ8CzzxeOIa0ud64GcRVJz7sfnHqdgJboXhSH81UV0CqSdTUEqNdUcbn0nttvvryJj0A+R3PpX+sV6Ayamcg0jXiZHmYAAAAA", + ); + // Guardian set 3 (expired). + program_test.add_account_with_base64_data( + find_guardian_set_address(u32::to_be_bytes(3), &CORE_BRIDGE_PID).0, + 3_647_040, + CORE_BRIDGE_PID, + "AwAAABMAAABYzDrlwJeyE848gZeeG5+VcHRqpf9suVJYm96GLCXvQ5ITL7nUpCFXEU3oRgGTvfOi/PgfhqCXZfR2L9EQegCGsy16CXeSaiBRMdhzHTnL64yCsv2C+u0nEdWa8PJJnRbnJvayEbOXVsBCRBvm2GULabVOvnFeI0NUzltNNI+3S5WOiWbi7D29SVinzRXnyvB8Tj3I58Rp+SyM2I+4AFogdKO/kTlT1pUmDYi8GqJaTu42PvAACsAHZyezX76i2sKP7lzLD+p2jq9FztE2udniSQNGSuiJ9cinI/wU+TEkt8c4hDy7iehkyGLDjN3Mz5XSzDek3ANqjSMrSPYs3UcxQS9IkNp5j2iWozMfZLSMEtHVf9nL5wgRcaob4dNsr+OGeRD5nAnjR4mcGcOBkrbnOHzNdoJ3wX2rG3pQJ8CzzxeOIa0ud64GcRVJz7sfnHqdgJboXhSH81UV0CqSdTUEqNdUcbn0nttvvryJj0A+R3PpX+sV6Ayamcg0jUA8xWP46h9m", + ); + program_test.prefer_bpf(true); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index d7c8835d5..127d21141 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -7,7 +7,7 @@ use std::cell::RefCell; use anchor_spl::token::spl_token::{self, instruction::approve}; use super::{airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}}; use super::mint::MintFixture; -use super::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router}; +use super::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router, initialise_post_message_shims, initialise_verify_shims}; use super::airdrop::airdrop; use super::account_fixtures::FixtureAccounts; @@ -59,6 +59,14 @@ impl PreTestingContext { PreTestingContext { program_test, testing_actors, program_data_pubkey, account_fixtures } } + + pub fn add_post_message_shims(&mut self) { + initialise_post_message_shims(&mut self.program_test); + } + + pub fn add_verify_shims(&mut self) { + initialise_verify_shims(&mut self.program_test); + } } diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs new file mode 100644 index 000000000..522ef20aa --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -0,0 +1,340 @@ +use anchor_lang::prelude::*; +use super::constants::*; +use wormhole_svm_shim::{post_message, verify_vaa}; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + hash::Hash, + message::{v0::Message, VersionedMessage}, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, + transaction::VersionedTransaction, +}; +use solana_program_test::ProgramTestContext; +use std::rc::Rc; +use std::cell::RefCell; +use wormhole_svm_definitions::{ + solana::Finality, + find_emitter_sequence_address, + find_shim_message_address, +}; +use base64::Engine; +use matching_engine::state::Auction; +use matching_engine::instruction::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimIx; + +struct BumpCosts { + message: u64, + sequence: u64, +} + +fn bump_cu_cost(bump: u8) -> u64 { + 1_500 * (255 - u64::from(bump)) +} + +const EMITTER_SEQUENCE_SEED: &[u8] = b"Sequence"; + +pub async fn set_up_post_message_transaction_test(test_ctx: &Rc>, payer_signer: &Rc, emitter_signer: &Rc, recent_blockhash: Hash) { + let (transaction, bump_costs) = set_up_post_message_transaction( + b"All your base are belong to us", + &payer_signer.clone().to_owned(), + &emitter_signer.clone().to_owned(), + recent_blockhash, + ); + let details = { + let out = test_ctx.borrow_mut().banks_client + .simulate_transaction(transaction) + .await + .unwrap(); + assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); + out.simulation_details.unwrap() + }; + let logs = details.logs; + let is_core_bridge_cpi_log = |line: &String| { + line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) + }; + // CPI to Core Bridge. + assert_eq!( + logs.iter() + .filter(|line| { + line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) + }) + .count(), + 1 + ); + assert_eq!( + logs.iter() + .filter(|line| { line.contains("Program log: Sequence: 0") }) + .count(), + 1 + ); + let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); + + // Self CPI. + assert_eq!( + logs.iter() + .skip(core_bridge_log_index) + .filter(|line| { + line.contains( + format!("Program {} invoke [2]", WORMHOLE_POST_MESSAGE_SHIM_PID).as_str(), + ) + }) + .count(), + 1 + ); + + // Wormhole Core Bridge re-derives the sequence account when it needs to be + // created (cool). So we need to subtract the sequence bump cost twice for + // the first message. + assert_eq!( + details.units_consumed - bump_costs.message - 2 * bump_costs.sequence, + // 53_418 + 46_076 + ); +} + +fn set_up_post_message_transaction( + payload: &[u8], + payer_signer: &Keypair, + emitter_signer: &Keypair, + recent_blockhash: Hash, +) -> (VersionedTransaction, BumpCosts) { + let emitter = emitter_signer.pubkey(); + let payer = payer_signer.pubkey(); + + // Use an invalid message if provided. + let (message, message_bump) = find_shim_message_address( + &emitter, + &WORMHOLE_POST_MESSAGE_SHIM_PID, + ); + + // Use an invalid core bridge program if provided. + let core_bridge_program = CORE_BRIDGE_PID; + + let (sequence, sequence_bump) = + find_emitter_sequence_address(&emitter, &core_bridge_program); + + let transfer_fee_ix = + solana_sdk::system_instruction::transfer(&payer, &CORE_BRIDGE_FEE_COLLECTOR, 100); + let post_message_ix = post_message::PostMessage { + program_id: &WORMHOLE_POST_MESSAGE_SHIM_PID, + accounts: post_message::PostMessageAccounts { + emitter: &emitter, + payer: &payer, + wormhole_program_id: &core_bridge_program, + derived: post_message::PostMessageDerivedAccounts { + message: Some(&message), + sequence: Some(&sequence), + ..Default::default() + }, + }, + data: post_message::PostMessageData::new( + 420, + Finality::Finalized, + payload, + ) + .unwrap(), + } + .instruction(); + + // Adding compute budget instructions to ensure all instructions fit into + // one transaction. + // + // NOTE: Invoking the compute budget costs in total 300 CU. + let message = Message::try_compile( + &payer, + &[ + transfer_fee_ix, + post_message_ix, + ComputeBudgetInstruction::set_compute_unit_price(420), + ComputeBudgetInstruction::set_compute_unit_limit(100_000), + ], + &[], + recent_blockhash, + ) + .unwrap(); + + let transaction = VersionedTransaction::try_new( + VersionedMessage::V0(message), + &[payer_signer, emitter_signer], + ) + .unwrap(); + + ( + transaction, + BumpCosts { + message: bump_cu_cost(message_bump), + sequence: bump_cu_cost(sequence_bump), + }, + ) +} + +const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; + +/// Post signatures before the auction is created. +pub async fn set_up_verify_shims_test(test_ctx: &Rc>, payer_signer: &Rc) -> Result { + let guardian_signatures_signer = Keypair::new(); + let (transaction, decoded_vaa)= set_up_verify_shims_transaction(test_ctx, payer_signer); + + let details = { + let out = test_ctx.borrow_mut().banks_client + .simulate_transaction(transaction) + .await + .unwrap(); + assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); + assert_eq!( + out.simulation_details.clone().unwrap().units_consumed, + // 13_355 + 3_337 + ); + out.simulation_details.unwrap() + }; + + { + let out = test_ctx.borrow_mut().banks_client + .process_transaction(transaction) + .await; + assert!(out.is_ok()); + out.unwrap(); + }; + + // Check guardian signatures account after processing the transaction. + let guardian_signatures_info = test_ctx.borrow_mut().banks_client + .get_account(guardian_signatures_signer.pubkey()) + .await + .unwrap() + .unwrap(); + + let account_data = &guardian_signatures_info.data; + let (expected_length, expected_guardian_signatures_data) = + generate_expected_guardian_signatures_info( + &payer_signer.pubkey(), + decoded_vaa.total_signatures, + decoded_vaa.guardian_set_index, + decoded_vaa.guardian_signatures, + ); + + assert_eq!(account_data.len(), expected_length); + assert_eq!( + wormhole_svm_definitions::borsh::deserialize_with_discriminator::(&account_data[..]).unwrap(), + expected_guardian_signatures_data + ); + Ok(guardian_signatures_signer.pubkey()) +} + +struct DecodedVaa { + pub guardian_set_index: u32, + pub total_signatures: u8, + pub guardian_signatures: Vec<[u8; GUARDIAN_SIGNATURE_LENGTH]>, + pub body: Vec, +} + +impl From<&str> for DecodedVaa { + fn from(vaa: &str) -> Self { + let mut buf = base64::prelude::BASE64_STANDARD.decode(vaa).unwrap(); + let guardian_set_index = u32::from_be_bytes(buf[1..5].try_into().unwrap()); + let total_signatures = buf[5]; + + let body = buf + .drain((6 + total_signatures as usize * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH)..) + .collect(); + + let mut guardian_signatures = Vec::with_capacity(total_signatures as usize); + + for i in 0..usize::from(total_signatures) { + let offset = 6 + i * 66; + let mut signature = [0; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; + signature.copy_from_slice(&buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]); + guardian_signatures.push(signature); + } + + Self { + guardian_set_index, + total_signatures, + guardian_signatures, + body, + } + } +} + +fn set_up_verify_shims_transaction(test_ctx: &Rc>, payer_signer: &Rc) -> (VersionedTransaction, DecodedVaa) { + let decoded_vaa = DecodedVaa::from(VAA); + assert_eq!(decoded_vaa.total_signatures, 13); + let recent_blockhash = test_ctx.borrow().last_blockhash; + let guardian_signatures_signer = Keypair::new(); + let guardian_signatures_slice: &[[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; 13] = &decoded_vaa.guardian_signatures.try_into().unwrap(); + + let mut post_signatures_ix = verify_vaa::PostSignatures { + program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, + accounts: verify_vaa::PostSignaturesAccounts { + payer: &payer_signer.pubkey(), + guardian_signatures: &guardian_signatures_signer.pubkey(), + }, + data: verify_vaa::PostSignaturesData::new( + decoded_vaa.guardian_set_index, + decoded_vaa.total_signatures, + guardian_signatures_slice, + ), + } + .instruction(); + + let message = Message::try_compile( + &tx_payer, + &[ + post_signatures_ix, + ComputeBudgetInstruction::set_compute_unit_price(69), + // NOTE: CU limit is higher than needed to resolve errors in test. + ComputeBudgetInstruction::set_compute_unit_limit(25_000), + ], + &[], + recent_blockhash, + ) + .unwrap(); + + ( + VersionedTransaction::try_new( + VersionedMessage::V0(message), + &[payer_signer, guardian_signatures_signer], + ) + .unwrap(), + decoded_vaa, + ) +} + +fn generate_expected_guardian_signatures_info( + payer: &Pubkey, + total_signatures: u8, + guardian_set_index: u32, + guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, +) -> ( + usize, // expected length + GuardianSignatures, +) { + let expected_length = { + 8 // discriminator + + 32 // refund recipient + + 4 // guardian set index + + 4 // guardian signatures length + + (total_signatures as usize) * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH + }; + + let guardian_signatures = GuardianSignatures { + refund_recipient: *payer, + guardian_set_index_be: guardian_set_index.to_be_bytes(), + guardian_signatures, + }; + + (expected_length, guardian_signatures) +} + +pub fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, guardian_signatures_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey) -> Result<()> { + let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id).0; + let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; + let guardian_set_pubkey = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); + let (guardian_set, guardian_set_bump) = Pubkey::find_program_address(&[wormhole_svm_definitions::GUARDIAN_SET_SEED, guardian_signatures.guardian_index_be_slice()], &wormhole_program_id); + let place_initial_offer_ix = PlaceInitialOfferCctpShimIx { + offer_price: 1__000_000, + guardian_set_bump: 0, + vaa_message: VaaMessage::new(0, 0, 0, 0, 0, vec![]), + }; + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 26304a067..75894f9d8 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -1,9 +1,9 @@ use anchor_lang::prelude::*; use common::messages::{FastMarketOrder, SlowOrderResponse}; use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; -use common::wormhole_cctp_solana::wormhole::VaaAccount; // TODO: Remove this if not needed use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; // TODO: Remove this if not needed +use secp256k1::SecretKey as SecpSecretKey; use super::constants::Chain; use super::CHAIN_TO_DOMAIN; @@ -98,6 +98,12 @@ impl PostedVaaData { ]) } + pub fn sign_with_guardian_set(&self, guardian_secret_key: &SecpSecretKey) -> [u8; 64] { + // Sign the message hash with the guardian key + let signature = guardian_secret_key.sign_ecdsa(self.message_hash().as_ref()); + signature.serialize_compact() + } + pub fn digest(&self) -> [u8; 32] { keccak::hashv(&[self.message_hash().as_ref()]).as_ref().try_into().unwrap() } From 17f8bb7a0af6eb6d513293f7e3281803771cda6e Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 24 Feb 2025 19:01:52 +0000 Subject: [PATCH 009/112] working shim test --- solana/programs/matching-engine/Cargo.toml | 9 +- .../matching-engine/src/composite/mod.rs | 4 - solana/programs/matching-engine/src/lib.rs | 1 + .../auction/offer/place_initial/cctp_shim.rs | 69 ++++---- .../tests/initialize_integration_tests.rs | 32 +++- .../matching-engine/tests/utils/auction.rs | 4 +- .../matching-engine/tests/utils/constants.rs | 5 +- .../tests/utils/program_fixtures.rs | 4 +- .../matching-engine/tests/utils/shims.rs | 165 +++++++++++++----- .../matching-engine/tests/utils/vaa.rs | 32 +++- 10 files changed, 225 insertions(+), 100 deletions(-) diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 68ea744f2..497bf27af 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -40,11 +40,8 @@ hex.workspace = true ruint.workspace = true cfg-if.workspace = true wormhole-svm-definitions.workspace = true - - -# wormhole-bridge-solana.workspace = true - - +wormhole-svm-shim.workspace = true +wormhole-io.workspace = true [dev-dependencies] hex-literal.workspace = true @@ -57,7 +54,7 @@ base64 = "0.22.1" lazy_static = "1.4.0" bs58 = "0.5.0" serde = { version = "1.0.212", features = ["derive"] } -secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context"] } +secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context", "recovery"] } wormhole-svm-shim.workspace = true wormhole-svm-definitions.workspace = true diff --git a/solana/programs/matching-engine/src/composite/mod.rs b/solana/programs/matching-engine/src/composite/mod.rs index e994d4ed5..940328dc3 100644 --- a/solana/programs/matching-engine/src/composite/mod.rs +++ b/solana/programs/matching-engine/src/composite/mod.rs @@ -18,10 +18,6 @@ use common::{ wormhole::{core_bridge_program, VaaAccount}, }, }; -use wormhole_svm_bridge::{ - GuardianSet, - GuardianSetSignatures, -}; #[derive(Accounts)] pub struct Usdc<'info> { diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 14f36ba93..d16ab1b85 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -9,6 +9,7 @@ mod events; mod processor; pub use processor::InitializeArgs; +pub use processor::VaaMessage; use processor::*; pub mod state; diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index 7b83f0b0f..c6a939f00 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -6,15 +6,15 @@ use crate::{ }; use anchor_lang::prelude::*; use anchor_spl::token; -use common::{messages::{raw::LiquidityLayerMessage, FastMarketOrder}, TRANSFER_AUTHORITY_SEED_PREFIX}; -use solana_sdk::program::invoke_signed_unchecked; -use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashData}; +use common::{messages::{raw::LiquidityLayerMessage, FastMarketOrder}, wormhole_io::WriteableBytes, TRANSFER_AUTHORITY_SEED_PREFIX}; +use wormhole_svm_shim::verify_vaa::{GuardianSetPubkey, VerifyHash, VerifyHashAccounts, VerifyHashData}; use common::wormhole_io::TypePrefixedPayload; -use solana_program::{keccak, instruction::Instruction, program::invoke_signed}; +use solana_program::{keccak, instruction::Instruction, program::invoke_signed, program::invoke_signed_unchecked}; +use wormhole_io::Readable; #[derive(Accounts)] -#[instruction(offer_price: u64)] +#[instruction(offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage)] #[event_cpi] pub struct PlaceInitialOfferCctpShim<'info> { #[account(mut)] @@ -61,7 +61,7 @@ pub struct PlaceInitialOfferCctpShim<'info> { space = 8 + Auction::INIT_SPACE, seeds = [ Auction::SEED_PREFIX, - fast_order_path.fast_vaa.load_unchecked().digest().as_ref(), + vaa_message.digest().as_ref(), ], bump )] @@ -99,20 +99,26 @@ pub struct PlaceInitialOfferCctpShim<'info> { token_program: Program<'info, token::Token>, } +// TODO: Change this to be PlaceInitialOfferArgs and go from there ... /// A vaa message is the serialised message body of a posted vaa. Only the fields that are required to create the digest are included. -pub struct VaaMessage(Vec); +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct VaaMessage(pub Vec); impl VaaMessage { pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32], payload: Vec) -> Self { Self(VaaMessageBody::new(consistency_level, vaa_time, sequence, emitter_chain, emitter_address, payload).to_vec()) } + pub fn from_vec(vec: Vec) -> Self { + Self(vec) + } + fn message_hash(&self) -> keccak::Hash { keccak::hashv(&[self.0.as_ref()]) } - pub fn digest(&self) -> [u8; 32] { - self.message_hash().as_ref().try_into().unwrap() + pub fn digest(&self) -> keccak::Hash { + keccak::hashv(&[self.message_hash().as_ref()]) } fn payload(&self) -> Vec { @@ -171,7 +177,7 @@ impl Payload { max_fee, init_auction_fee, deadline, - redeemer_message, + redeemer_message: WriteableBytes::new(redeemer_message), }; Self(fast_market_order.to_vec()) } @@ -242,44 +248,45 @@ pub fn place_initial_offer_cctp_shim( guardian_set_bump: u8, vaa_message: VaaMessage, ) -> Result<()> { + msg!("Placing initial offer with CCTP shim"); // Extract the guardian set and guardian set signatures accounts from the FastOrderPathShim. - let FastOrderPathShim{guardian_set, guardian_set_signatures, live_router_path} = ctx.accounts.fast_order_path_shim; - + let FastOrderPathShim{guardian_set, guardian_set_signatures, live_router_path} = &ctx.accounts.fast_order_path_shim; + msg!("Made fast order path shim"); // Check that the VAA message corresponds to the accounts in the FastOrderPathShim. - let from_endpoint = live_router_path.from_endpoint; + let from_endpoint = &live_router_path.from_endpoint; assert_eq!(from_endpoint.chain, vaa_message.emitter_chain()); assert_eq!(from_endpoint.address, vaa_message.emitter_address()); + msg!("Asserted equal emitter chain and address"); let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message.digest()); - // Call the verify shim program using cpi to verify the hash of the shim. - let verify_shim_ix = Instruction { + let verify_shim_ix = VerifyHash { program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: vec![ - AccountMeta::new(guardian_set.key(), false), - AccountMeta::new(guardian_set_signatures.key(), false), - ], - data: verify_hash_data.to_vec(), - }; - + accounts: VerifyHashAccounts { + guardian_set: GuardianSetPubkey::Provided(&guardian_set.key()), + guardian_signatures: &guardian_set_signatures.key(), + }, + data: verify_hash_data + }.instruction(); + msg!("Made verify shim ix"); // Make the cpi call to verify the shim. invoke_signed_unchecked(&verify_shim_ix, &[ guardian_set.to_account_info(), guardian_set_signatures.to_account_info(), - ], &[&[]])?; + ], &[])?; + msg!("Verified shim"); + let payload = vaa_message.payload(); + + let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&payload).unwrap(); - let order = LiquidityLayerMessage::try_from(vaa_message.payload()) - .unwrap() - .to_fast_market_order_unchecked(); - // Parse the transfer amount from the VAA. - let amount_in = order.amount_in(); + let amount_in = order.amount_in; // Saturating to u64::MAX is safe here. If the amount really ends up being this large, the // checked addition below will catch it. let security_deposit = order - .max_fee() + .max_fee .saturating_add(utils::auction::compute_notional_security_deposit( &ctx.accounts.auction_config, amount_in, @@ -290,7 +297,7 @@ pub fn place_initial_offer_cctp_shim( let initial_offer_token = ctx.accounts.offer_token.key(); ctx.accounts.auction.set_inner(Auction { bump: ctx.bumps.auction, - vaa_hash: vaa_message.digest(), + vaa_hash: vaa_message.digest().as_ref().try_into().unwrap(), vaa_timestamp: vaa_message.vaa_time(), target_protocol: live_router_path.to_endpoint.protocol, status: AuctionStatus::Active, @@ -306,7 +313,7 @@ pub fn place_initial_offer_cctp_shim( amount_in, security_deposit, offer_price, - redeemer_message_len: order.redeemer_message_len(), + redeemer_message_len: order.redeemer_message.len() as u16, destination_asset_info: Default::default(), } .into(), diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 53035feac..da1dfa274 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -10,8 +12,10 @@ use utils::account_fixtures::FixtureAccounts; use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; use utils::setup::{PreTestingContext, TestingContext}; use utils::vaa::{create_vaas_test, create_vaas_test_with_chain_and_address}; -use utils::shims::{set_up_post_message_transaction_test, set_up_verify_shims_test}; +use utils::shims::{add_guardian_signatures_account, place_initial_offer_shim, set_up_post_message_transaction_test, set_up_verify_shims_test}; use utils::constants::*; +use wormhole_svm_definitions::borsh::GuardianSignatures; +use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { @@ -124,7 +128,7 @@ pub async fn test_setup_vaas() { let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( - fast_vaa, // Fast VAA pubkey + Some(fast_vaa), // Fast VAA pubkey solver.clone(), // Solver auction_config_address.clone(), // Auction config pubkey arb_endpoint_address, // From router endpoint pubkey @@ -156,6 +160,8 @@ pub async fn test_post_message_shims() { set_up_post_message_transaction_test(&testing_context.test_context, &payer_signer, &emitter_signer, recent_blockhash).await; } + +// TODO: Check that you cannot execute the order the old way and then place the initial offer using the shim #[tokio::test] pub async fn test_verify_shims() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); @@ -165,13 +171,12 @@ pub async fn test_verify_shims() { let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - set_up_verify_shims_test(&testing_context.test_context, &testing_context.testing_actors.owner.keypair()).await; + // TODO: Change the posting of the signatures to be the actual single guardian signature. let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; let first_test_ft = vaas_test.0.first().unwrap(); // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim - // TODO: Load this from a file - let guardian_secret_key = SecpSecretKey::load("cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0").expect("Failed to load guardian secret key"); + let guardian_secret_key = SecpSecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to load guardian secret key"); let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction let usdc_mint_address = USDC_MINT_ADDRESS; @@ -190,9 +195,8 @@ pub async fn test_verify_shims() { let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; let solver = testing_context.testing_actors.solvers[0].clone(); - let fast_vaa = Pubkey::new_unique(); // This is only needed in order to be compatible with the AuctionAccounts struct let auction_accounts = AuctionAccounts::new( - fast_vaa, // Fast VAA pubkey + None, // Fast VAA pubkey solver.clone(), // Solver auction_config_address.clone(), // Auction config pubkey arb_endpoint_address, // From router endpoint pubkey @@ -201,8 +205,18 @@ pub async fn test_verify_shims() { usdc_mint_address, // USDC mint pubkey ); - let fast_market_order = first_test_ft.fast_transfer_vaa.clone(); + let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - let initial_offer_fixture = place_initial_offer(&testing_context.test_context, &auction_accounts, fast_market_order, testing_context.testing_actors.owner.keypair(), PROGRAM_ID).await; + + let solver = testing_context.testing_actors.solvers[0].clone(); + let initial_offer_fixture = place_initial_offer_shim( + &testing_context.test_context, + &testing_context.testing_actors.owner.keypair(), + &PROGRAM_ID, + &CORE_BRIDGE_PROGRAM_ID, + &vaa_data, + testing_context.testing_actors.solvers[0].clone(), + &auction_accounts, + ).await.expect("Failed to place initial offer"); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 92e919720..02b4611ed 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -17,7 +17,7 @@ use super::setup::Solver; use super::vaa::TestVaa; pub struct AuctionAccounts { - pub fast_vaa: Pubkey, + pub fast_vaa: Option, pub offer_token: Pubkey, pub solver: Solver, pub auction_config: Pubkey, @@ -28,7 +28,7 @@ pub struct AuctionAccounts { } impl AuctionAccounts { - pub fn new(fast_vaa: Pubkey, solver: Solver, auction_config: Pubkey, from_router_endpoint: Pubkey, to_router_endpoint: Pubkey, custodian: Pubkey, usdc_mint_address: Pubkey) -> Self { + pub fn new(fast_vaa: Option, solver: Solver, auction_config: Pubkey, from_router_endpoint: Pubkey, to_router_endpoint: Pubkey, custodian: Pubkey, usdc_mint_address: Pubkey) -> Self { Self { fast_vaa, offer_token: solver.token_account_address().unwrap(), diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index e19327145..1e9f49d12 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -54,6 +54,8 @@ cfg_if::cfg_if! { pub const USDC_MINT: Pubkey = pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); } } + +pub const GUARDIAN_SECRET_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); @@ -103,8 +105,7 @@ pub fn get_player_one_keypair() -> Keypair { // Other constants #[allow(dead_code)] pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111111111111111111111111115"); -#[allow(dead_code)] -pub const GUARDIAN_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; + #[allow(dead_code)] pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; diff --git a/solana/programs/matching-engine/tests/utils/program_fixtures.rs b/solana/programs/matching-engine/tests/utils/program_fixtures.rs index d265cb9e1..66a2d05e1 100644 --- a/solana/programs/matching-engine/tests/utils/program_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/program_fixtures.rs @@ -78,14 +78,14 @@ pub fn initialise_verify_shims(program_test: &mut ProgramTest) { ); // Guardian set 4 (active). program_test.add_account_with_base64_data( - find_guardian_set_address(u32::to_be_bytes(4), &CORE_BRIDGE_PID).0, + wormhole_svm_definitions::find_guardian_set_address(u32::to_be_bytes(4), &CORE_BRIDGE_PID).0, 3_647_040, CORE_BRIDGE_PID, "BAAAABMAAABYk7WnbD9zlkVkiIW9zMBs1wo80/9suVJYm96GLCXvQ5ITL7nUpCFXEU3oRgGTvfOi/PgfhqCXZfR2L9EQegCGsy16CXeSaiBRMdhzHTnL64yCsv2C+u0nEdWa8PJJnRbnJvayEbOXVsBCRBvm2GULabVOvnFeI0NUzltNNI+3S5WOiWbi7D29SVinzRXnyvB8Tj3I58Rp+SyM2I+4AFogdKO/kTlT1pUmDYi8GqJaTu42PvAACsAHZyezX76i2sKP7lzLD+p2jq9FztE2udniSQNGSuiJ9cinI/wU+TEkt8c4hDy7iehkyGLDjN3Mz5XSzDek3ANqjSMrSPYs3UcxQS9IkNp5j2iWozMfZLSMEtHVf9nL5wgRcaob4dNsr+OGeRD5nAnjR4mcGcOBkrbnOHzNdoJ3wX2rG3pQJ8CzzxeOIa0ud64GcRVJz7sfnHqdgJboXhSH81UV0CqSdTUEqNdUcbn0nttvvryJj0A+R3PpX+sV6Ayamcg0jXiZHmYAAAAA", ); // Guardian set 3 (expired). program_test.add_account_with_base64_data( - find_guardian_set_address(u32::to_be_bytes(3), &CORE_BRIDGE_PID).0, + wormhole_svm_definitions::find_guardian_set_address(u32::to_be_bytes(3), &CORE_BRIDGE_PID).0, 3_647_040, CORE_BRIDGE_PID, "AwAAABMAAABYzDrlwJeyE848gZeeG5+VcHRqpf9suVJYm96GLCXvQ5ITL7nUpCFXEU3oRgGTvfOi/PgfhqCXZfR2L9EQegCGsy16CXeSaiBRMdhzHTnL64yCsv2C+u0nEdWa8PJJnRbnJvayEbOXVsBCRBvm2GULabVOvnFeI0NUzltNNI+3S5WOiWbi7D29SVinzRXnyvB8Tj3I58Rp+SyM2I+4AFogdKO/kTlT1pUmDYi8GqJaTu42PvAACsAHZyezX76i2sKP7lzLD+p2jq9FztE2udniSQNGSuiJ9cinI/wU+TEkt8c4hDy7iehkyGLDjN3Mz5XSzDek3ANqjSMrSPYs3UcxQS9IkNp5j2iWozMfZLSMEtHVf9nL5wgRcaob4dNsr+OGeRD5nAnjR4mcGcOBkrbnOHzNdoJ3wX2rG3pQJ8CzzxeOIa0ud64GcRVJz7sfnHqdgJboXhSH81UV0CqSdTUEqNdUcbn0nttvvryJj0A+R3PpX+sV6Ayamcg0jUA8xWP46h9m", diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 522ef20aa..87d8ee2fc 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -1,5 +1,5 @@ use anchor_lang::prelude::*; -use super::constants::*; +use super::{constants::*, setup::Solver}; use wormhole_svm_shim::{post_message, verify_vaa}; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, @@ -8,10 +8,10 @@ use solana_sdk::{ pubkey::Pubkey, signature::Keypair, signer::Signer, - transaction::VersionedTransaction, + transaction::{Transaction, VersionedTransaction}, }; use solana_program_test::ProgramTestContext; -use std::rc::Rc; +use std::{rc::Rc, str::FromStr}; use std::cell::RefCell; use wormhole_svm_definitions::{ solana::Finality, @@ -19,8 +19,12 @@ use wormhole_svm_definitions::{ find_shim_message_address, }; use base64::Engine; -use matching_engine::state::Auction; +use matching_engine::{accounts::{CheckedCustodian, FastOrderPathShim, LiveRouterEndpoint, LiveRouterPath}, state::Auction}; use matching_engine::instruction::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimIx; +use matching_engine::accounts::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimAccounts; +use anchor_lang::InstructionData; +use solana_sdk::instruction::Instruction; +use wormhole_svm_definitions::borsh::GuardianSignatures; struct BumpCosts { message: u64, @@ -82,14 +86,6 @@ pub async fn set_up_post_message_transaction_test(test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32) -> Result { + let recent_blockhash = test_ctx.borrow().last_blockhash; + + let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, recent_blockhash); + + test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to add guardian signatures account"); + + Ok(signatures_signer.pubkey()) +} /// Post signatures before the auction is created. pub async fn set_up_verify_shims_test(test_ctx: &Rc>, payer_signer: &Rc) -> Result { - let guardian_signatures_signer = Keypair::new(); - let (transaction, decoded_vaa)= set_up_verify_shims_transaction(test_ctx, payer_signer); + let guardian_signatures_signer = Rc::new(Keypair::new()); + let (transaction, decoded_vaa)= set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); let details = { let out = test_ctx.borrow_mut().banks_client - .simulate_transaction(transaction) + .simulate_transaction(transaction.clone()) .await .unwrap(); assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); @@ -221,10 +225,11 @@ pub async fn set_up_verify_shims_test(test_ctx: &Rc> Ok(guardian_signatures_signer.pubkey()) } +#[derive(Clone)] struct DecodedVaa { pub guardian_set_index: u32, pub total_signatures: u8, - pub guardian_signatures: Vec<[u8; GUARDIAN_SIGNATURE_LENGTH]>, + pub guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, pub body: Vec, } @@ -256,13 +261,17 @@ impl From<&str> for DecodedVaa { } } -fn set_up_verify_shims_transaction(test_ctx: &Rc>, payer_signer: &Rc) -> (VersionedTransaction, DecodedVaa) { +fn set_up_verify_shims_transaction(test_ctx: &Rc>, payer_signer: &Rc, guardian_signatures_signer: &Rc) -> (VersionedTransaction, DecodedVaa) { + const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; let decoded_vaa = DecodedVaa::from(VAA); + let decoded_vaa_clone = decoded_vaa.clone(); assert_eq!(decoded_vaa.total_signatures, 13); let recent_blockhash = test_ctx.borrow().last_blockhash; - let guardian_signatures_signer = Keypair::new(); - let guardian_signatures_slice: &[[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; 13] = &decoded_vaa.guardian_signatures.try_into().unwrap(); + let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = &decoded_vaa.guardian_signatures; + (post_signatures_transaction(payer_signer, guardian_signatures_signer, decoded_vaa.guardian_set_index, decoded_vaa.total_signatures, guardian_signatures_vec, recent_blockhash), decoded_vaa_clone) +} +fn post_signatures_transaction(payer_signer: &Rc, guardian_signatures_signer: &Rc, guardian_set_index: u32, total_signatures: u8, guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, recent_blockhash: Hash) -> VersionedTransaction { let mut post_signatures_ix = verify_vaa::PostSignatures { program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, accounts: verify_vaa::PostSignaturesAccounts { @@ -270,15 +279,15 @@ fn set_up_verify_shims_transaction(test_ctx: &Rc>, p guardian_signatures: &guardian_signatures_signer.pubkey(), }, data: verify_vaa::PostSignaturesData::new( - decoded_vaa.guardian_set_index, - decoded_vaa.total_signatures, - guardian_signatures_slice, + guardian_set_index, + total_signatures, + guardian_signatures_vec.as_slice(), ), } .instruction(); let message = Message::try_compile( - &tx_payer, + &payer_signer.pubkey(), &[ post_signatures_ix, ComputeBudgetInstruction::set_compute_unit_price(69), @@ -290,14 +299,11 @@ fn set_up_verify_shims_transaction(test_ctx: &Rc>, p ) .unwrap(); - ( - VersionedTransaction::try_new( - VersionedMessage::V0(message), - &[payer_signer, guardian_signatures_signer], - ) - .unwrap(), - decoded_vaa, + VersionedTransaction::try_new( + VersionedMessage::V0(message), + &[payer_signer, guardian_signatures_signer], ) + .unwrap() } fn generate_expected_guardian_signatures_info( @@ -326,15 +332,94 @@ fn generate_expected_guardian_signatures_info( (expected_length, guardian_signatures) } -pub fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, guardian_signatures_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey) -> Result<()> { - let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id).0; +pub struct PlaceInitialOfferShimFixture { + pub auction_address: Pubkey, + pub auction_custody_token_address: Pubkey, + pub guardian_set_pubkey: Pubkey, + pub guardian_signatures_pubkey: Pubkey, + +} + +pub async fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { + // The auction address? is this needed? + let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_data.digest()], &program_id).0; let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; - let guardian_set_pubkey = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); - let (guardian_set, guardian_set_bump) = Pubkey::find_program_address(&[wormhole_svm_definitions::GUARDIAN_SET_SEED, guardian_signatures.guardian_index_be_slice()], &wormhole_program_id); - let place_initial_offer_ix = PlaceInitialOfferCctpShimIx { - offer_price: 1__000_000, - guardian_set_bump: 0, - vaa_message: VaaMessage::new(0, 0, 0, 0, 0, vec![]), + let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); + + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); + let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); + let signatures_signer = Rc::new(Keypair::new()); + let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); + + let guardian_signatures = GuardianSignatures { + refund_recipient: payer_signer.pubkey(), + guardian_set_index_be: 0_u32.to_be_bytes(), + guardian_signatures: vec![guardian_set_signatures], + }; + + let vaa_message = matching_engine::VaaMessage::from_vec(vaa_data.message_vec()); + + println!("Vaa message length: {}", vaa_message.0.len()); + + let offer_price = 1__000_000; + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimIx { + offer_price, + guardian_set_bump, + vaa_message, + }.data(); + + // Approve the transfer authority + let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; + solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; + let checked_custodian = CheckedCustodian { + custodian: accounts.custodian, }; - Ok(()) + + let fast_order_path_shim = FastOrderPathShim { + guardian_set: guardian_set_pubkey, + guardian_set_signatures: guardian_signatures_pubkey.clone().to_owned(), + live_router_path: LiveRouterPath { + from_endpoint: LiveRouterEndpoint { + endpoint: accounts.from_router_endpoint, + }, + to_endpoint: LiveRouterEndpoint { + endpoint: accounts.to_router_endpoint, + }, + }, + }; + + let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; + + let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimAccounts { + payer: payer_signer.pubkey(), + transfer_authority, + custodian: checked_custodian, + auction_config: accounts.auction_config, + fast_order_path_shim, + auction: auction_address, + offer_token: accounts.offer_token, + auction_custody_token: auction_custody_token_address, + usdc: matching_engine::accounts::Usdc { mint: accounts.usdc_mint }, + verify_vaa_shim_program: WORMHOLE_VERIFY_VAA_SHIM_PID, + system_program: solana_program::system_program::ID, + token_program: anchor_spl::token::spl_token::ID, + event_authority, + program: program_id.clone().to_owned(), + }; + let place_initial_offer_ix = Instruction { + program_id: program_id.clone().to_owned(), + accounts: place_initial_offer_ix_accounts.to_account_metas(Some(false)), + data: place_initial_offer_ix_data, + }; + let recent_blockhash = test_ctx.borrow().last_blockhash; + let transaction = Transaction::new_signed_with_payer(&[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); + + test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to place initial offer"); + + Ok(PlaceInitialOfferShimFixture { + auction_address, + auction_custody_token_address, + guardian_set_pubkey, + guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), + }) } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 75894f9d8..bd27cb867 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -2,7 +2,9 @@ use anchor_lang::prelude::*; use common::messages::{FastMarketOrder, SlowOrderResponse}; use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload -use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; // TODO: Remove this if not needed +use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; use secp256k1::ecdsa::RecoverableSignature; +// TODO: Remove this if not needed +use secp256k1::ecdsa::RecoverableSignature as RecovorableSecpSignature; use secp256k1::SecretKey as SecpSecretKey; use super::constants::Chain; @@ -98,10 +100,32 @@ impl PostedVaaData { ]) } - pub fn sign_with_guardian_set(&self, guardian_secret_key: &SecpSecretKey) -> [u8; 64] { + pub fn message_vec(&self) -> Vec { + vec![ + self.vaa_time.to_be_bytes().as_ref(), + self.nonce.to_be_bytes().as_ref(), + self.emitter_chain.to_be_bytes().as_ref(), + &self.emitter_address, + &self.sequence.to_be_bytes(), + &[self.consistency_level], + self.payload.as_ref(), + ].concat() + } + + pub fn sign_with_guardian_key(&self, guardian_secret_key: &SecpSecretKey, index: u8) -> [u8; 66] { // Sign the message hash with the guardian key - let signature = guardian_secret_key.sign_ecdsa(self.message_hash().as_ref()); - signature.serialize_compact() + let secp = secp256k1::SECP256K1; + let msg = secp256k1::Message::from_digest(self.digest()); + let recoverable_signature = secp.sign_ecdsa_recoverable(&msg, &guardian_secret_key); + let mut signature_bytes = [0u8; 66]; + // First byte is the index + signature_bytes[0] = index; + // Next 64 bytes are the signature in compact format + let (recovery_id, compact_sig) = recoverable_signature.serialize_compact(); + // Recovery ID goes in byte 65 + signature_bytes[1..65].copy_from_slice(&compact_sig); + signature_bytes[65] = i32::from(recovery_id) as u8; + signature_bytes } pub fn digest(&self) -> [u8; 32] { From 521f7d70fd30760af5070f9d5cd87c602d16f823 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 24 Feb 2025 19:36:56 +0000 Subject: [PATCH 010/112] removed warnings --- .../auction/offer/place_initial/cctp_shim.rs | 36 ++----------- .../tests/initialize_integration_tests.rs | 19 +++---- .../matching-engine/tests/utils/airdrop.rs | 3 -- .../matching-engine/tests/utils/auction.rs | 5 +- .../matching-engine/tests/utils/constants.rs | 3 +- .../matching-engine/tests/utils/router.rs | 2 + .../matching-engine/tests/utils/setup.rs | 2 +- .../matching-engine/tests/utils/shims.rs | 18 +++---- .../matching-engine/tests/utils/vaa.rs | 54 +++++++++---------- 9 files changed, 52 insertions(+), 90 deletions(-) diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index c6a939f00..92d6a4fd7 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -1,16 +1,15 @@ use crate::{ composite::*, error::MatchingEngineError, - state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, MessageProtocol}, + state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus}, utils, }; use anchor_lang::prelude::*; use anchor_spl::token; -use common::{messages::{raw::LiquidityLayerMessage, FastMarketOrder}, wormhole_io::WriteableBytes, TRANSFER_AUTHORITY_SEED_PREFIX}; +use common::{messages::FastMarketOrder, TRANSFER_AUTHORITY_SEED_PREFIX}; use wormhole_svm_shim::verify_vaa::{GuardianSetPubkey, VerifyHash, VerifyHashAccounts, VerifyHashData}; use common::wormhole_io::TypePrefixedPayload; -use solana_program::{keccak, instruction::Instruction, program::invoke_signed, program::invoke_signed_unchecked}; -use wormhole_io::Readable; +use solana_program::{keccak, program::invoke_signed_unchecked}; #[derive(Accounts)] @@ -140,6 +139,7 @@ impl VaaMessage { u32::from_be_bytes(self.0[0..4].try_into().unwrap()) } + #[allow(dead_code)] fn nonce(&self) -> u32 { // nonce is the next 4 bytes of the message u32::from_be_bytes(self.0[4..8].try_into().unwrap()) @@ -163,26 +163,6 @@ impl VaaMessage { } -pub struct Payload(Vec); - -impl Payload { - pub fn new(amount_in: u64, min_amount_out: u64, target_chain: u16, redeemer: [u8; 32], sender: [u8; 32], refund_address: [u8; 32], max_fee: u64, init_auction_fee: u64, deadline: u32, redeemer_message: Vec) -> Self { - let fast_market_order = FastMarketOrder { - amount_in, - min_amount_out, - target_chain, - redeemer, - sender, - refund_address, - max_fee, - init_auction_fee, - deadline, - redeemer_message: WriteableBytes::new(redeemer_message), - }; - Self(fast_market_order.to_vec()) - } -} - /// Just a helper struct to make the code more readable. struct VaaMessageBody { @@ -192,12 +172,6 @@ struct VaaMessageBody { /// Time the vaa was submitted pub vaa_time: u32, - /// Account where signatures are stored - pub vaa_signature_account: Pubkey, - - /// Time the posted message was created - pub submission_time: u32, - /// Unique nonce for this message pub nonce: u32, @@ -219,8 +193,6 @@ impl VaaMessageBody { Self { consistency_level, vaa_time, - vaa_signature_account: Pubkey::new_unique(), // Doesn't matter for the hash - submission_time: 0, // Doesn't matter for the hash nonce: 0, // Always 0 sequence, emitter_chain, // Can be taken from the live router path diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index da1dfa274..44fab6e04 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,20 +1,14 @@ -use std::str::FromStr; - use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; -use secp256k1::SecretKey as SecpSecretKey; mod utils; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; -use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test, get_router_endpoint_address}; +use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; use utils::initialize::initialize_program; -use utils::account_fixtures::FixtureAccounts; use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; use utils::setup::{PreTestingContext, TestingContext}; -use utils::vaa::{create_vaas_test, create_vaas_test_with_chain_and_address}; -use utils::shims::{add_guardian_signatures_account, place_initial_offer_shim, set_up_post_message_transaction_test, set_up_verify_shims_test}; -use utils::constants::*; -use wormhole_svm_definitions::borsh::GuardianSignatures; +use utils::vaa::create_vaas_test_with_chain_and_address; +use utils::shims::{place_initial_offer_shim, set_up_post_message_transaction_test}; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { @@ -142,7 +136,7 @@ pub async fn test_setup_vaas() { let initial_offer_fixture = place_initial_offer(&testing_context.test_context, &auction_accounts, fast_market_order, testing_context.testing_actors.owner.keypair(), PROGRAM_ID).await; initial_offer_fixture.verify_initial_offer(&testing_context.test_context).await; - let improved_offer_fixture = improve_offer(&testing_context.test_context, initial_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; + let _improved_offer_fixture = improve_offer(&testing_context.test_context, initial_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } @@ -176,7 +170,6 @@ pub async fn test_verify_shims() { let first_test_ft = vaas_test.0.first().unwrap(); // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim - let guardian_secret_key = SecpSecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to load guardian secret key"); let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction let usdc_mint_address = USDC_MINT_ADDRESS; @@ -210,13 +203,13 @@ pub async fn test_verify_shims() { let solver = testing_context.testing_actors.solvers[0].clone(); - let initial_offer_fixture = place_initial_offer_shim( + let _initial_offer_fixture = place_initial_offer_shim( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &PROGRAM_ID, &CORE_BRIDGE_PROGRAM_ID, &vaa_data, - testing_context.testing_actors.solvers[0].clone(), + solver, &auction_accounts, ).await.expect("Failed to place initial offer"); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index bc441062d..53d10924a 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -1,5 +1,3 @@ -use anchor_lang::prelude::*; -use anchor_lang::ToAccountInfo; use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; use std::rc::Rc; @@ -49,7 +47,6 @@ pub async fn airdrop( pub async fn airdrop_usdc( test_context: &Rc>, recipient_ata: &Pubkey, - owner: &Pubkey, amount: u64, ) { let usdc_mint_address = constants::USDC_MINT; diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 02b4611ed..c11c33bcb 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; -use matching_engine::state::{Auction, AuctionConfig, AuctionInfo}; -use matching_engine::instruction::{CreateNewAuctionHistory, CreateFirstAuctionHistory, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, ImproveOffer as ImproveOfferIx}; +use matching_engine::state::{Auction, AuctionInfo}; +use matching_engine::instruction::{PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, ImproveOffer as ImproveOfferIx}; use matching_engine::accounts::{ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc}; use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; use solana_sdk::instruction::Instruction; @@ -12,7 +12,6 @@ use solana_sdk::transaction::Transaction; use solana_sdk::signature::{Keypair, Signer}; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use anchor_lang::InstructionData; -use solana_sdk::program_pack::Pack; use super::setup::Solver; use super::vaa::TestVaa; diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 1e9f49d12..1859b1696 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -1,9 +1,10 @@ +#![allow(dead_code)] + use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; use solana_program::pubkey; // Program IDs - cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { /// Core Bridge program ID on Solana mainnet. diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 5766fe541..6b74fafce 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -92,6 +92,7 @@ impl TestRouterEndpoints { Self { arbitrum, ethereum, solana } } + #[allow(dead_code)] pub fn get_endpoint_info(&self, chain: Chain) -> TestEndpointInfo { match chain { Chain::Arbitrum => self.arbitrum.info.clone(), @@ -101,6 +102,7 @@ impl TestRouterEndpoints { } } + #[allow(dead_code)] pub fn get_endpoint_address(&self, chain: Chain) -> Pubkey { match chain { Chain::Arbitrum => self.arbitrum.endpoint_address, diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 127d21141..99cabc287 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -211,7 +211,7 @@ impl TestingActors { async fn create_atas(&mut self, test_context: &Rc>, usdc_mint_address: Pubkey) { for actor in self.token_account_actors() { let usdc_ata = create_token_account(test_context.clone(), &actor.keypair(), &usdc_mint_address).await; - airdrop_usdc(test_context, &usdc_ata.address, &actor.pubkey(), 420_000__000_000).await; + airdrop_usdc(test_context, &usdc_ata.address, 420_000__000_000).await; actor.token_account = Some(usdc_ata); } } diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 87d8ee2fc..3e2c7099e 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -26,6 +26,7 @@ use anchor_lang::InstructionData; use solana_sdk::instruction::Instruction; use wormhole_svm_definitions::borsh::GuardianSignatures; +#[allow(dead_code)] struct BumpCosts { message: u64, sequence: u64, @@ -35,10 +36,11 @@ fn bump_cu_cost(bump: u8) -> u64 { 1_500 * (255 - u64::from(bump)) } +#[allow(dead_code)] const EMITTER_SEQUENCE_SEED: &[u8] = b"Sequence"; pub async fn set_up_post_message_transaction_test(test_ctx: &Rc>, payer_signer: &Rc, emitter_signer: &Rc, recent_blockhash: Hash) { - let (transaction, bump_costs) = set_up_post_message_transaction( + let (transaction, _bump_costs) = set_up_post_message_transaction( b"All your base are belong to us", &payer_signer.clone().to_owned(), &emitter_signer.clone().to_owned(), @@ -174,12 +176,13 @@ pub async fn add_guardian_signatures_account(test_ctx: &Rc>, payer_signer: &Rc) -> Result { let guardian_signatures_signer = Rc::new(Keypair::new()); let (transaction, decoded_vaa)= set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); - let details = { + let _details = { let out = test_ctx.borrow_mut().banks_client .simulate_transaction(transaction.clone()) .await @@ -226,6 +229,7 @@ pub async fn set_up_verify_shims_test(test_ctx: &Rc> } #[derive(Clone)] +#[allow(dead_code)] struct DecodedVaa { pub guardian_set_index: u32, pub total_signatures: u8, @@ -261,6 +265,7 @@ impl From<&str> for DecodedVaa { } } +#[allow(dead_code)] fn set_up_verify_shims_transaction(test_ctx: &Rc>, payer_signer: &Rc, guardian_signatures_signer: &Rc) -> (VersionedTransaction, DecodedVaa) { const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; let decoded_vaa = DecodedVaa::from(VAA); @@ -272,7 +277,7 @@ fn set_up_verify_shims_transaction(test_ctx: &Rc>, p } fn post_signatures_transaction(payer_signer: &Rc, guardian_signatures_signer: &Rc, guardian_set_index: u32, total_signatures: u8, guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, recent_blockhash: Hash) -> VersionedTransaction { - let mut post_signatures_ix = verify_vaa::PostSignatures { + let post_signatures_ix = verify_vaa::PostSignatures { program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, accounts: verify_vaa::PostSignaturesAccounts { payer: &payer_signer.pubkey(), @@ -306,6 +311,7 @@ fn post_signatures_transaction(payer_signer: &Rc, guardian_signatures_s .unwrap() } +#[allow(dead_code)] fn generate_expected_guardian_signatures_info( payer: &Pubkey, total_signatures: u8, @@ -350,12 +356,6 @@ pub async fn place_initial_offer_shim(test_ctx: &Rc> let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); let signatures_signer = Rc::new(Keypair::new()); let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); - - let guardian_signatures = GuardianSignatures { - refund_recipient: payer_signer.pubkey(), - guardian_set_index_be: 0_u32.to_be_bytes(), - guardian_signatures: vec![guardian_set_signatures], - }; let vaa_message = matching_engine::VaaMessage::from_vec(vaa_data.message_vec()); diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index bd27cb867..7aa4fd62d 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -2,9 +2,6 @@ use anchor_lang::prelude::*; use common::messages::{FastMarketOrder, SlowOrderResponse}; use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload -use matching_engine::accounts::{FastOrderPath, LiquidityLayerVaa, LiveRouterPath}; use secp256k1::ecdsa::RecoverableSignature; -// TODO: Remove this if not needed -use secp256k1::ecdsa::RecoverableSignature as RecovorableSecpSignature; use secp256k1::SecretKey as SecpSecretKey; use super::constants::Chain; @@ -132,11 +129,7 @@ impl PostedVaaData { keccak::hashv(&[self.message_hash().as_ref()]).as_ref().try_into().unwrap() } - pub fn create_vaa_account(&self, program_test: &mut ProgramTest) -> Pubkey { - let vaa_hash = self.message_hash(); - let vaa_hash_as_slice = vaa_hash.as_ref(); - let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; - + pub fn create_vaa_account(&self, program_test: &mut ProgramTest, vaa_address: Pubkey) { let vaa_data_serialized = serialize_with_discriminator(self).unwrap(); let lamports = solana_sdk::rent::Rent::default().minimum_balance(vaa_data_serialized.len()); let vaa_account = Account { @@ -147,7 +140,6 @@ impl PostedVaaData { rent_epoch: u64::MAX, }; program_test.add_account(vaa_address, vaa_account); - vaa_address } } @@ -206,12 +198,17 @@ pub struct TestFastTransfer { } impl TestFastTransfer { - pub fn new(program_test: &mut ProgramTest, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> Self { - let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(program_test, token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient); - let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(program_test, start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence); + pub fn new(start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> Self { + let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient); + let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence); Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data } } } + pub fn add_to_test(&self, program_test:&mut ProgramTest) { + self.deposit_vaa.vaa_data.create_vaa_account(program_test, self.deposit_vaa.vaa_pubkey.clone()); + self.fast_transfer_vaa.vaa_data.create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey.clone()); + } + pub async fn verify_vaas(&self, test_context: &Rc>) { let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); @@ -231,7 +228,7 @@ impl TestFastTransfer { } } -pub fn create_deposit_message(program_test: &mut ProgramTest, token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> (Pubkey, PostedVaaData) { +pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> (Pubkey, PostedVaaData) { let slow_order_response = SlowOrderResponse { base_fee: 0, @@ -250,11 +247,13 @@ pub fn create_deposit_message(program_test: &mut ProgramTest, token_mint: Pubkey // Sequece == nonce in this case, since only vaas we are submitting are fast transfers let posted_vaa_data = PostedVaaData::new(source_address.chain, deposit.to_vec(), source_address.address, sequence, cctp_nonce as u32); - let vaa_address = posted_vaa_data.create_vaa_account(program_test); + let vaa_hash = posted_vaa_data.message_hash(); + let vaa_hash_as_slice = vaa_hash.as_ref(); + let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; (vaa_address, posted_vaa_data) } -pub fn create_fast_transfer_message(program_test: &mut ProgramTest, start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64) -> (Pubkey, PostedVaaData) { +pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64) -> (Pubkey, PostedVaaData) { // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); // Implements TypePrefixedPayload @@ -272,7 +271,9 @@ pub fn create_fast_transfer_message(program_test: &mut ProgramTest, start_timest }; let posted_vaa_data = PostedVaaData::new(source_address.chain, fast_market_order.to_vec(), source_address.address, sequence, cctp_nonce as u32); - let vaa_address = posted_vaa_data.create_vaa_account(program_test); + let vaa_hash = posted_vaa_data.message_hash(); + let vaa_hash_as_slice = vaa_hash.as_ref(); + let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; (vaa_address, posted_vaa_data) } @@ -294,35 +295,30 @@ impl TestFastTransfers { } /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer - pub fn add_ft(&mut self, program_test: &mut ProgramTest, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey) { + pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey) { let sequence = self.len() as u64; let cctp_nonce = sequence; - let test_fast_transfer = TestFastTransfer::new(program_test, start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient); + let test_fast_transfer = TestFastTransfer::new(start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient); self.0.push(test_fast_transfer); } } -pub fn create_vaas_test(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey) -> TestFastTransfers { - let mut test_fast_transfers = TestFastTransfers::new(); - let source_address = ChainAddress::new_unique(Chain::Arbitrum); - let destination_address = ChainAddress::new_unique(Chain::Ethereum); - let refund_address = source_address.clone(); - test_fast_transfers.add_ft(program_test, start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); - test_fast_transfers -} - pub fn create_vaas_test_with_chain_and_address(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32]) -> TestFastTransfers { let mut test_fast_transfers = TestFastTransfers::new(); let source_address = ChainAddress::new_with_address(source_chain, source_address); let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); let refund_address = source_address.clone(); - test_fast_transfers.add_ft(program_test, start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); + test_fast_transfers.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); + for test_fast_transfer in test_fast_transfers.0.iter() { + test_fast_transfer.add_to_test(program_test); + } test_fast_transfers } pub trait ToBytes { fn to_bytes(&self) ->[u8; 32]; } +#[allow(dead_code)] #[derive(Debug, Clone)] pub enum TestPubkey { Solana(Pubkey), @@ -396,6 +392,7 @@ pub struct ChainAddress { } impl ChainAddress { + #[allow(dead_code)] pub fn new_unique(chain: Chain) -> Self { match chain { Chain::Solana => Self { chain, address: TestPubkey::Solana(Pubkey::new_unique()) }, @@ -408,6 +405,7 @@ impl ChainAddress { } } + #[allow(dead_code)] pub fn new_with_address(chain: Chain, address: [u8; 32]) -> Self { Self { chain, address: TestPubkey::Bytes(address) } } From 3edc58fff70e2c8be50ee7b37902affda39102a7 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 27 Feb 2025 18:36:14 +0000 Subject: [PATCH 011/112] [working] test passes for fallback function --- solana/Cargo.lock | 28 + solana/Cargo.toml | 8 + solana/programs/matching-engine/Cargo.toml | 1 + .../matching-engine/src/composite/mod.rs | 1 - .../matching-engine/src/fallback/mod.rs | 2 + .../src/fallback/processor/create_account.rs | 120 ++++ .../src/fallback/processor/errors.rs | 31 + .../src/fallback/processor/mod.rs | 5 + .../fallback/processor/place_initial_offer.rs | 570 ++++++++++++++++++ .../fallback/processor/process_instruction.rs | 67 ++ solana/programs/matching-engine/src/lib.rs | 15 +- .../auction/offer/place_initial/cctp_shim.rs | 64 +- .../src/state/fast_market_order.rs | 49 ++ .../programs/matching-engine/src/state/mod.rs | 3 + .../tests/initialize_integration_tests.rs | 73 ++- .../matching-engine/tests/utils/auction.rs | 6 - .../matching-engine/tests/utils/mod.rs | 1 + .../matching-engine/tests/utils/router.rs | 1 - .../matching-engine/tests/utils/shims.rs | 228 +++++-- .../tests/utils/shims_execute_order.rs | 28 + 20 files changed, 1202 insertions(+), 99 deletions(-) create mode 100644 solana/programs/matching-engine/src/fallback/mod.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/create_account.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/errors.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/mod.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/process_instruction.rs create mode 100644 solana/programs/matching-engine/src/state/fast_market_order.rs create mode 100644 solana/programs/matching-engine/tests/utils/shims_execute_order.rs diff --git a/solana/Cargo.lock b/solana/Cargo.lock index e5132bfcc..a0acdb684 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -2539,6 +2539,7 @@ dependencies = [ "base64 0.22.1", "bincode", "bs58 0.5.0", + "bytemuck", "cfg-if", "hex", "hex-literal", @@ -2552,7 +2553,10 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", + "wormhole-io", "wormhole-solana-utils", + "wormhole-svm-definitions", + "wormhole-svm-shim", ] [[package]] @@ -3919,6 +3923,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + [[package]] name = "sha3" version = "0.9.1" @@ -6858,6 +6868,24 @@ dependencies = [ "wormhole-solana-consts", ] +[[package]] +name = "wormhole-svm-definitions" +version = "0.1.0" +dependencies = [ + "borsh 1.5.0", + "cfg-if", + "sha2-const-stable", + "solana-program", +] + +[[package]] +name = "wormhole-svm-shim" +version = "0.1.0" +dependencies = [ + "solana-program", + "wormhole-svm-definitions", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 7bc5ba4d9..fac4220a7 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -3,6 +3,7 @@ members = [ "modules/*", "programs/*" ] +exclude = ["lib"] resolver = "2" [workspace.package] @@ -47,6 +48,13 @@ hex = "0.4.3" ruint = "1.9.0" cfg-if = "1.0" hex-literal = "0.4.1" +bytemuck = "1.13.0" +wormhole-svm-shim = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "f69b3ae3" } +wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "f69b3ae3", features = ["borsh"] } + +[patch."https://github.com/wormholelabs-xyz/wormhole.git"] +wormhole-svm-shim = { path = "lib/wormhole/svm/wormhole-core-shims/crates/shim" } +wormhole-svm-definitions = { path = "lib/wormhole/svm/wormhole-core-shims/crates/definitions" } [profile.release] overflow-checks = true diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 497bf27af..05834e68d 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -37,6 +37,7 @@ anchor-spl.workspace = true solana-program.workspace = true hex.workspace = true +bytemuck.workspace = true ruint.workspace = true cfg-if.workspace = true wormhole-svm-definitions.workspace = true diff --git a/solana/programs/matching-engine/src/composite/mod.rs b/solana/programs/matching-engine/src/composite/mod.rs index 940328dc3..f3507f0f8 100644 --- a/solana/programs/matching-engine/src/composite/mod.rs +++ b/solana/programs/matching-engine/src/composite/mod.rs @@ -267,7 +267,6 @@ pub struct FastOrderPathShim<'info> { pub live_router_path: LiveRouterPath<'info>, } - #[derive(Accounts)] pub struct FastOrderPath<'info> { #[account( diff --git a/solana/programs/matching-engine/src/fallback/mod.rs b/solana/programs/matching-engine/src/fallback/mod.rs new file mode 100644 index 000000000..d52b9b264 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/mod.rs @@ -0,0 +1,2 @@ +mod processor; +pub use processor::*; diff --git a/solana/programs/matching-engine/src/fallback/processor/create_account.rs b/solana/programs/matching-engine/src/fallback/processor/create_account.rs new file mode 100644 index 000000000..4cdbf9b37 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/create_account.rs @@ -0,0 +1,120 @@ +use anchor_lang::prelude::*; +use solana_program::{ + entrypoint::ProgramResult, + program::invoke_signed_unchecked, + system_instruction, + instruction::{AccountMeta, Instruction}, +}; +pub fn create_account_reliably( + payer_key: &Pubkey, + account_key: &Pubkey, + current_lamports: u64, + data_len: usize, + accounts: &[AccountInfo], + program_id: &Pubkey, + signer_seeds: &[&[&[u8]]], +) -> ProgramResult { + let lamports = Rent::get().unwrap().minimum_balance(data_len); + + if current_lamports == 0 { + let ix = system_instruction::create_account( + payer_key, + account_key, + lamports, + data_len as u64, + program_id, + ); + + invoke_signed_unchecked(&ix, accounts, signer_seeds)?; + } else { + const MAX_CPI_DATA_LEN: usize = 36; + + // Perform up to three CPIs: + // 1. Transfer lamports from payer to account (may not be necessary). + // 2. Allocate data to the account. + // 3. Assign the account owner to this program. + // + // The max length of instruction data is 36 bytes among the three + // instructions, so we will reuse the same allocated memory for all. + let mut cpi_ix = Instruction { + program_id: solana_program::system_program::ID, + accounts: vec![ + AccountMeta::new(*payer_key, true), + AccountMeta::new(*account_key, true), + ], + data: Vec::with_capacity(MAX_CPI_DATA_LEN), + }; + + // Safety: Because capacity is > 12, it is safe to set this length. + unsafe { + cpi_ix.data.set_len(12); + } + + // We will have to transfer the remaining lamports needed to cover rent + // for the account. + let lamport_diff = lamports.saturating_sub(current_lamports); + + // Only invoke transfer if there are lamports required. + if lamport_diff != 0 { + let cpi_data = &mut cpi_ix.data; + + // Safety: Because the capacity is > 4, it is safe to write to the + // first 4 elements, which covers the System program instruction + // selectors. + // + // The transfer and allocate instructions are 12 bytes long: + // - 4 bytes for the discriminator + // - 8 bytes for the lamports (transfer) or data length (allocate) + // + // The last 8 bytes will be copied to the data slice. + unsafe { + core::ptr::write_bytes(cpi_data.as_mut_ptr(), 0, 4); + } + cpi_data[0] = 2; // transfer selector + cpi_data[4..12].copy_from_slice(&lamport_diff.to_le_bytes()); + + invoke_signed_unchecked(&cpi_ix, accounts, signer_seeds)?; + } + + let cpi_accounts = &mut cpi_ix.accounts; + + // Safety: Setting the length reduces the previous length from the last + // CPI call. + // + // Both allocate and assign instructions require one account (the + // account being created). + unsafe { + cpi_accounts.set_len(1); + } + + // Because the payer and account are writable signers, we can simply + // overwrite the pubkey of the first account. + cpi_accounts[0].pubkey = *account_key; + + { + let cpi_data = &mut cpi_ix.data; + + cpi_data[0] = 8; // allocate selector + cpi_data[4..12].copy_from_slice(&(data_len as u64).to_le_bytes()); + + invoke_signed_unchecked(&cpi_ix, accounts, signer_seeds)?; + } + + { + let cpi_data = &mut cpi_ix.data; + + // Safety: The capacity of this vector is 36. This data will be + // overwritten for the next CPI call. + unsafe { + cpi_data.set_len(MAX_CPI_DATA_LEN); + } + + cpi_data[0] = 1; // assign selector + cpi_data[4..36].copy_from_slice(&program_id.to_bytes()); + + invoke_signed_unchecked(&cpi_ix, accounts, signer_seeds)?; + } + } + + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/errors.rs b/solana/programs/matching-engine/src/fallback/processor/errors.rs new file mode 100644 index 000000000..677893013 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/errors.rs @@ -0,0 +1,31 @@ +use anchor_lang::prelude::*; + +// TODO: Move these into the matching engine error code enum +#[error_code] +pub enum FallbackError { + #[msg("Account is already initialized")] + AccountAlreadyInitialized, + + #[msg("From and to endpoints are the same")] + SameEndpoints, + + #[msg("Invalid PDA")] + InvalidPda, + + #[msg("Account data too small")] + AccountDataTooSmall, + + #[msg("Borsh Deserialization Error")] + BorshDeserializationError, + + #[msg("Invalid mint")] + InvalidMint, + + #[msg("Account not writable")] + AccountNotWritable, + + #[msg("Token transfer failed")] + TokenTransferFailed, + + // Add more error variants as needed +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs new file mode 100644 index 000000000..3eb9bfd82 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -0,0 +1,5 @@ +pub mod process_instruction; +pub use process_instruction::*; +pub mod place_initial_offer; +pub mod errors; +pub mod create_account; \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs new file mode 100644 index 000000000..d01b6134f --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -0,0 +1,570 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; +use bytemuck::{Pod, Zeroable}; +use common::messages::FastMarketOrder; +use solana_program::instruction::Instruction; +use solana_program::program::invoke_signed_unchecked; +use super::create_account::create_account_reliably; // TODO: Use this everywhere (but note that there is a bug with index zero on cpi) +use solana_program::keccak; +use anchor_lang::Discriminator; +use solana_program::program_pack::Pack; +use wormhole_io::TypePrefixedPayload; +use wormhole_svm_shim::verify_vaa::{GuardianSetPubkey, VerifyHash, VerifyHashAccounts, VerifyHashData}; +use crate::state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; +use common::TRANSFER_AUTHORITY_SEED_PREFIX; +use crate::ID as PROGRAM_ID; + +use super::FallbackMatchingEngineInstruction; +use super::errors::FallbackError; +use crate::error::MatchingEngineError; + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct PlaceInitialOfferCctpShimData { + pub offer_price: u64, + pub sequence: u64, + pub vaa_time: u32, + pub guardian_set_bump: u8, + pub consistency_level: u8, + _padding: [u8; 2], + pub fast_market_order: FastMarketOrderState, +} + +impl PlaceInitialOfferCctpShimData { + + pub fn new(offer_price: u64, sequence: u64, vaa_time: u32, guardian_set_bump: u8, consistency_level: u8, fast_market_order: FastMarketOrderState) -> Self { + Self { offer_price, sequence, vaa_time, guardian_set_bump, consistency_level, _padding: [0_u8; 2], fast_market_order } + } + + pub fn from_bytes(data: &[u8]) -> Option<&Self> { + bytemuck::try_from_bytes::(data).ok() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub struct PlaceInitialOfferCctpShimAccounts<'ix> { + pub signer: &'ix Pubkey, + pub transfer_authority: &'ix Pubkey, + pub custodian: &'ix Pubkey, + pub auction_config: &'ix Pubkey, + pub guardian_set: &'ix Pubkey, + pub guardian_set_signatures: &'ix Pubkey, + pub from_endpoint: &'ix Pubkey, + pub to_endpoint: &'ix Pubkey, + pub fast_market_order: &'ix Pubkey, // Needs initalising + pub auction: &'ix Pubkey, // Needs initalising + pub offer_token: &'ix Pubkey, + pub auction_custody_token: &'ix Pubkey, + pub usdc: &'ix Pubkey, + pub verify_vaa_shim_program: &'ix Pubkey, + pub system_program: &'ix Pubkey, + pub token_program: &'ix Pubkey, +} + +impl<'ix> PlaceInitialOfferCctpShimAccounts<'ix> { + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new(*self.signer, true), + AccountMeta::new(*self.transfer_authority, false), + AccountMeta::new(*self.custodian, false), + AccountMeta::new(*self.auction_config, false), + AccountMeta::new(*self.guardian_set, false), + AccountMeta::new(*self.guardian_set_signatures, false), + AccountMeta::new(*self.from_endpoint, false), + AccountMeta::new(*self.to_endpoint, false), + AccountMeta::new(*self.fast_market_order, false), + AccountMeta::new(*self.auction, false), + AccountMeta::new(*self.offer_token, false), + AccountMeta::new(*self.auction_custody_token, false), + AccountMeta::new(*self.usdc, false), + AccountMeta::new(*self.verify_vaa_shim_program, false), + AccountMeta::new(*self.system_program, false), + AccountMeta::new(*self.token_program, false), + ] + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PlaceInitialOfferCctpShim<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: PlaceInitialOfferCctpShimAccounts<'ix>, + pub data: PlaceInitialOfferCctpShimData, +} + +impl PlaceInitialOfferCctpShim<'_> { + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(&self.data).to_vec(), + } + } +} + +#[derive(Debug)] +pub struct VaaMessageBodyHeader { + pub consistency_level: u8, + pub vaa_time: u32, + pub nonce: u32, + pub sequence: u64, + pub emitter_chain: u16, + pub emitter_address: [u8; 32], +} + +impl VaaMessageBodyHeader { + pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32]) -> Self { + Self { consistency_level, vaa_time, nonce: 0, sequence, emitter_chain, emitter_address } + } + + pub fn message_body(&self, fast_market_order: &FastMarketOrderState) -> Vec { + let mut message_body = vec![]; + message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); + message_body.extend_from_slice(&self.nonce.to_be_bytes()); + message_body.extend_from_slice(&self.emitter_chain.to_be_bytes()); + message_body.extend_from_slice(&self.emitter_address); + message_body.extend_from_slice(&self.sequence.to_be_bytes()); + message_body.extend_from_slice(&[self.consistency_level]); + let mut payload = vec![]; + payload.push(11_u8); + payload.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.redeemer); + payload.extend_from_slice(&fast_market_order.sender); + payload.extend_from_slice(&fast_market_order.refund_address); + payload.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); + payload.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); + if fast_market_order.redeemer_message_length > 0 { + payload.extend_from_slice(&fast_market_order.redeemer_message[..fast_market_order.redeemer_message_length as usize]); + } + message_body.extend_from_slice(&payload); + message_body + } + + pub fn message_hash(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { + keccak::hashv(&[ + self.message_body(fast_market_order).as_ref() + ]) + } + + pub fn digest(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { + keccak::hashv(&[self.message_hash(fast_market_order).as_ref()]) + } + + pub fn vaa_time(&self) -> u32 { + self.vaa_time + } + + pub fn sequence(&self) -> u64 { + self.sequence + } + + pub fn emitter_chain(&self) -> u16 { + self.emitter_chain + } +} + +pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { + // Check account owners + let program_id = &crate::ID; // Your program ID + // Check all accounts are valid + if accounts.len() < 16 { + return Err(ErrorCode::AccountNotEnoughKeys.into()); + } + // Extract data fields + let PlaceInitialOfferCctpShimData { + offer_price, + guardian_set_bump, + sequence, + vaa_time, + consistency_level, + _padding, + fast_market_order: fast_market_order_zero_copy, + } = *data; + + let signer = &accounts[0]; + let transfer_authority = &accounts[1]; + let custodian = &accounts[2]; + let auction_config = &accounts[3]; + let guardian_set = &accounts[4]; + let guardian_set_signatures = &accounts[5]; + let from_endpoint = &accounts[6]; + let to_endpoint = &accounts[7]; + let fast_market_order_account = &accounts[8]; + let auction_account = &accounts[9]; + let auction_key = auction_account.key(); + let offer_token = &accounts[10]; + let auction_custody_token = &accounts[11]; + let usdc = &accounts[12]; + // let verify_vaa_shim_program = &accounts[13]; + // let system_program = &accounts[14]; + // let token_program = &accounts[15]; + + // Check pda of the transfer authority is valid + let transfer_authority_seeds = [ + TRANSFER_AUTHORITY_SEED_PREFIX, + auction_key.as_ref(), + &offer_price.to_be_bytes() + ]; + let (transfer_authority_pda, transfer_authority_bump) = Pubkey::find_program_address(&transfer_authority_seeds, &PROGRAM_ID); + if transfer_authority_pda != transfer_authority.key() { + msg!("Transfer authority pda is invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((transfer_authority_pda, transfer_authority.key()))); + } + + // Check custodian owner + if custodian.owner != program_id { + msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + } + // Check custodian is not paused + let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; + if checked_custodian.paused { + msg!("Custodian is paused"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + } + // Check auction_config owner + if auction_config.owner != program_id { + msg!("Auction config owner is invalid: expected {}, got {}", program_id, auction_config.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("auction_config")); + } + + // Check auction config id is correct corresponding to the custodian + let auction_config_account = AuctionConfig::try_deserialize(&mut &auction_config.data.borrow()[..])?; + if auction_config_account.id != checked_custodian.auction_config_id { + msg!("Auction config id is invalid"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("auction_config")); + } + + // Check usdc mint + if usdc.key() != common::USDC_MINT { + msg!("Usdc mint is invalid"); + return Err(FallbackError::InvalidMint.into()); + } + + // Check from_endpoint owner + if from_endpoint.owner != program_id { + msg!("From endpoint owner is invalid: expected {}, got {}", program_id, from_endpoint.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("from_endpoint")); + } + + // Deserialise the from_endpoint account + let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; + + // Check to_endpoint owner + if to_endpoint.owner != program_id { + msg!("To endpoint owner is invalid: expected {}, got {}", program_id, to_endpoint.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("to_endpoint")); + } + + // Deserialise the to_endpoint account + let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; + + // Check that the from and to endpoints are different + if from_endpoint_account.chain == to_endpoint_account.chain { + return Err(MatchingEngineError::SameEndpoint.into()); + } + + // Check that the to endpoint protocol is cctp or local + match to_endpoint_account.protocol { + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), + _ => return Err(MatchingEngineError::InvalidEndpoint.into()), + } + + // Check that the from endpoint protocol is cctp or local + match from_endpoint_account.protocol { + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), + _ => return Err(MatchingEngineError::InvalidEndpoint.into()), + } + + // Check that to endpoint chain is equal to the fast_market_order target_chain + if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { + msg!("To endpoint chain is not equal to the fast_market_order target_chain"); + return Err(MatchingEngineError::InvalidTargetRouter.into()); + } + + // Check if the fast_market_order account is already initialized + if !fast_market_order_account.data_is_empty() { + msg!("Fast market order account is already initialized"); + return Err(FallbackError::AccountAlreadyInitialized.into()); + } + + // Check contents of fast_market_order + { + let deadline = fast_market_order_zero_copy.deadline as i64; + let expiration = i64::from(vaa_time).saturating_add(crate::VAA_AUCTION_EXPIRATION_TIME); + let current_time = Clock::get().unwrap().unix_timestamp; + if !((deadline == 0 || current_time < deadline) && current_time < expiration) { + msg!("Fast market order has expired"); + return Err(MatchingEngineError::FastMarketOrderExpired.into()); + } + + if offer_price > fast_market_order_zero_copy.max_fee { + msg!("Offer price is too high"); + return Err(MatchingEngineError::OfferPriceTooHigh.into()); + } + } + let fast_market_order_key = fast_market_order_account.key(); + let redeemer = fast_market_order_zero_copy.redeemer; + let sender = fast_market_order_zero_copy.sender; + let refund_address = fast_market_order_zero_copy.refund_address; + + let payload = FastMarketOrder { + amount_in: fast_market_order_zero_copy.amount_in, + min_amount_out: fast_market_order_zero_copy.min_amount_out, + target_chain: fast_market_order_zero_copy.target_chain, + redeemer, + sender, + refund_address, + max_fee: fast_market_order_zero_copy.max_fee, + init_auction_fee: fast_market_order_zero_copy.init_auction_fee, + deadline: fast_market_order_zero_copy.deadline, + redeemer_message: fast_market_order_zero_copy.redeemer_message[..fast_market_order_zero_copy.redeemer_message_length as usize].to_vec().try_into().unwrap(), + }.to_vec(); + // Create the vaa_message struct to get the digest + let vaa_message = VaaMessageBodyHeader::new(consistency_level, vaa_time, sequence, from_endpoint_account.chain, from_endpoint_account.address); + let vaa_message_digest = vaa_message.digest(&fast_market_order_zero_copy); + + // Begin of initialisation of auction custody token account + // ------------------------------------------------------------------------------------------------ + let auction_custody_token_space = spl_token::state::Account::LEN; + let rent = Rent::get()?; + let lamports = rent.minimum_balance(auction_custody_token_space); + + let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address(&[crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_key.as_ref()], &program_id); + if auction_custody_token_pda != auction_custody_token.key() { + msg!("Auction custody token pda is invalid. Passed account: {}, expected: {}", auction_custody_token.key(), auction_custody_token_pda); + return Err(FallbackError::InvalidPda.into()); + } + + let auction_custody_token_seeds = [ + crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_key.as_ref(), + &[auction_custody_token_bump], + ]; + let auction_custody_token_signer_seeds = &[&auction_custody_token_seeds[..]]; + // TODO: Must use create_account_reliably + create_account_reliably( + &signer.key(), + &auction_custody_token_pda, + auction_custody_token.lamports(), + auction_custody_token_space, + accounts, + &spl_token::ID, + auction_custody_token_signer_seeds, + )?; + // Initialise the token account + let init_token_account_ix = spl_token::instruction::initialize_account3( + &spl_token::ID, + &auction_custody_token_pda, + &usdc.key(), + &auction_account.key(), + ).unwrap(); + + solana_program::program::invoke( + &init_token_account_ix, + accounts, + ).unwrap(); + + // ------------------------------------------------------------------------------------------------ + // End of initialisation of auction custody token account + + // Begin of initialisation of fast_market_order account + // ------------------------------------------------------------------------------------------------ + let space = 8 + std::mem::size_of::(); + let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + vaa_message_digest.as_ref(), + ], + &program_id, + ); + + if fast_market_order_pda != fast_market_order_key { + msg!("Fast market order pda is invalid"); + return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); + } + let fast_market_order_seeds = [ + FastMarketOrderState::SEED_PREFIX, + vaa_message_digest.as_ref(), + &[fast_market_order_bump], + ]; + let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; + // Create the account using the system program + create_account_reliably( + &signer.key(), + &fast_market_order_key, + fast_market_order_account.lamports(), + space, + accounts, + &program_id, + fast_market_order_signer_seeds, + )?; + // Borrow the account data mutably + let mut data = fast_market_order_account.try_borrow_mut_data()?; + + // Write the discriminator to the first 8 bytes + let discriminator = FastMarketOrderState::discriminator(); + data[0..8].copy_from_slice(&discriminator); + + let fast_market_order_bytes = bytemuck::bytes_of(&fast_market_order_zero_copy); + + // Ensure the destination has enough space + if data.len() < 8 + fast_market_order_bytes.len() { + msg!("Account data buffer too small"); + return Err(FallbackError::AccountDataTooSmall.into()); + } + + // Write the fast_market_order struct to the account + data[8..8 + fast_market_order_bytes.len()].copy_from_slice(fast_market_order_bytes); + // ------------------------------------------------------------------------------------------------ + // End of initialisation of fast_market_order account + + // Begin of initialisation of auction account + // ------------------------------------------------------------------------------------------------ + let auction_space = 8 + Auction::INIT_SPACE; + let (pda, bump) = Pubkey::find_program_address( + &[ + Auction::SEED_PREFIX, + vaa_message_digest.as_ref(), + ], + &program_id, + ); + + if pda != auction_key { + msg!("Auction pda is invalid"); + return Err(FallbackError::InvalidPda.into()); + } + let auction_seeds = [ + Auction::SEED_PREFIX, + vaa_message_digest.as_ref(), + &[bump], + ]; + let auction_signer_seeds = &[&auction_seeds[..]]; + create_account_reliably( + &signer.key(), + &auction_key, + auction_account.lamports(), + auction_space, + accounts, + &program_id, + auction_signer_seeds, + )?; + // Borrow the account data mutably + let mut data = auction_account.try_borrow_mut_data().map_err(|_| FallbackError::AccountNotWritable)?; + + // Write the discriminator to the first 8 bytes + let discriminator = Auction::discriminator(); + data[0..8].copy_from_slice(&discriminator); + + let security_deposit = fast_market_order_zero_copy.max_fee + .saturating_add(crate::utils::auction::compute_notional_security_deposit( + &auction_config_account.parameters, + fast_market_order_zero_copy.amount_in, + )); + + let auction_to_write = Auction { + bump, + vaa_hash: vaa_message.digest(&fast_market_order_zero_copy).as_ref().try_into().unwrap(), + vaa_timestamp: vaa_message.vaa_time(), + target_protocol: to_endpoint_account.protocol, + status: AuctionStatus::Active, + prepared_by: signer.key(), + info: AuctionInfo { + config_id: auction_config_account.id, + custody_token_bump: auction_custody_token_bump, + vaa_sequence: vaa_message.sequence(), + source_chain: vaa_message.emitter_chain(), + best_offer_token: offer_token.key(), + initial_offer_token: offer_token.key(), + start_slot: Clock::get().unwrap().slot, + amount_in: fast_market_order_zero_copy.amount_in, + security_deposit, + offer_price, + redeemer_message_len: fast_market_order_zero_copy.redeemer_message_length, + destination_asset_info: Default::default(), + } + .into(), + }; + // Write the auction struct to the account + let auction_bytes = auction_to_write.try_to_vec().map_err(|_| FallbackError::BorshDeserializationError)?; + data[8..8 + auction_bytes.len()].copy_from_slice(&auction_bytes); + // ------------------------------------------------------------------------------------------------ + // End of initialisation of auction account + + // Start of cpi call to verify the shim. + // ------------------------------------------------------------------------------------------------ + let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message_digest.clone()); + let verify_shim_ix = VerifyHash { + program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: VerifyHashAccounts { + guardian_set: GuardianSetPubkey::Provided(&guardian_set.key()), + guardian_signatures: &guardian_set_signatures.key(), + }, + data: verify_hash_data + }.instruction(); + // Make the cpi call to verify the shim. + invoke_signed_unchecked(&verify_shim_ix, &[ + guardian_set.to_account_info(), + guardian_set_signatures.to_account_info(), + ], &[])?; + // ------------------------------------------------------------------------------------------------ + // End of cpi call to verify the shim. + + // Start of token transfer from offer token to auction custody token + // ------------------------------------------------------------------------------------------------ + + let transfer_ix = spl_token::instruction::transfer( + &spl_token::ID, + &offer_token.key(), + &auction_custody_token.key(), + &transfer_authority.key(), + &[], // Apparently this is only for multi-sig accounts + fast_market_order_zero_copy.amount_in + .checked_add(security_deposit) + .ok_or_else(|| MatchingEngineError::U64Overflow)? + ).unwrap(); + invoke_signed_unchecked(&transfer_ix, accounts, &[&[ + TRANSFER_AUTHORITY_SEED_PREFIX, + auction_key.as_ref(), + &offer_price.to_be_bytes(), + &[transfer_authority_bump], + ]]).map_err(|_| FallbackError::TokenTransferFailed)?; + // ------------------------------------------------------------------------------------------------ + // End of token transfer from offer token to auction custody token + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bytemuck() { + let test_fast_market_order = FastMarketOrderState { + amount_in: 1000000000000000000, + min_amount_out: 1000000000000000000, + deadline: 1000000000, + target_chain: 1, + redeemer_message_length: 0, + redeemer: [0_u8; 32], + sender: [0_u8; 32], + refund_address: [0_u8; 32], + max_fee: 0, + init_auction_fee: 0, + redeemer_message: [0_u8; 512], + }; + let bytes = bytemuck::bytes_of(&test_fast_market_order); + assert!(bytes.len() == std::mem::size_of::()); + } + +} diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs new file mode 100644 index 000000000..3c833e52c --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -0,0 +1,67 @@ + +use crate::ID; +use anchor_lang::prelude::*; +use wormhole_svm_definitions::make_anchor_discriminator; +use super::place_initial_offer::PlaceInitialOfferCctpShimData; +use super::place_initial_offer::place_initial_offer_cctp_shim; + + + +impl<'ix> FallbackMatchingEngineInstruction<'ix> { + pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); +} + +pub enum FallbackMatchingEngineInstruction<'ix> { + PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), +} + +pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { + if program_id != &ID { + return Err(ErrorCode::InvalidProgramId.into()); + } + + let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data).unwrap(); + match instruction { + FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { + place_initial_offer_cctp_shim(accounts, &data) + } + } +} + +impl<'ix> FallbackMatchingEngineInstruction<'ix> { + pub fn deserialize(instruction_data: &'ix [u8]) -> Option { + if instruction_data.len() < 8 { + return None; + } + + match instruction_data[..8].try_into().unwrap() { + FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { + Some(Self::PlaceInitialOfferCctpShim(&PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) + }, + _ => None, + } + } +} + +impl FallbackMatchingEngineInstruction<'_> { + pub fn to_vec(&self) -> Vec { + match self { + Self::PlaceInitialOfferCctpShim(data) => { + // Calculate the total capacity needed + let data_slice = bytemuck::bytes_of(*data); + let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + + // Create a vector with the calculated capacity + let mut out = Vec::with_capacity(total_capacity); + + // Add the selector + out.extend_from_slice(&FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR); + + // Extend the vector with the data slice + out.extend_from_slice(data_slice); + + out + }, + } + } +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index d16ab1b85..6194e9d80 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -3,7 +3,7 @@ mod composite; -mod error; +pub mod error; mod events; @@ -14,6 +14,8 @@ use processor::*; pub mod state; +pub mod fallback; + pub mod utils; pub use utils::admin::AddCctpRouterEndpointArgs; @@ -258,9 +260,9 @@ pub mod matching_engine { /// This instruction is used to create a new auction given a valid `VaaShim`. /// This instruction should act in the exact same way as `place_initial_offer_cctp` except that /// it will check the digest of the vaa directly using a cpi call to the verify shim program. - pub fn place_initial_offer_cctp_shim(ctx: Context, offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage) -> Result<()> { - processor::place_initial_offer_cctp_shim(ctx, offer_price, guardian_set_bump, vaa_message) - } + // pub fn place_initial_offer_cctp_shim(ctx: Context, offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage) -> Result<()> { + // processor::place_initial_offer_cctp_shim(ctx, offer_price, guardian_set_bump, vaa_message) + // } /// This instruction is used to improve an existing auction offer. The `offer_price` must be /// greater than the current `offer_price` in the auction. This instruction will revert if the @@ -482,6 +484,11 @@ pub mod matching_engine { pub fn add_auction_history_entry(_ctx: Context) -> Result<()> { err!(ErrorCode::Deprecated) } + + /// Non anchor function for placing an initial offer using the VAA shim. + pub fn fallback_process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { + fallback::process_instruction(program_id, accounts, instruction_data) + } } #[derive(Accounts)] diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index 92d6a4fd7..332d2fe1b 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -1,7 +1,7 @@ use crate::{ composite::*, error::MatchingEngineError, - state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus}, + state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, FastMarketOrder as FastMarketOrderState}, utils, }; use anchor_lang::prelude::*; @@ -14,7 +14,6 @@ use solana_program::{keccak, program::invoke_signed_unchecked}; #[derive(Accounts)] #[instruction(offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage)] -#[event_cpi] pub struct PlaceInitialOfferCctpShim<'info> { #[account(mut)] payer: Signer<'info>, @@ -52,6 +51,21 @@ pub struct PlaceInitialOfferCctpShim<'info> { /// The cpi instruction will verify the hash of the fast order path so no account constraints are needed. fast_order_path_shim: FastOrderPathShim<'info>, + #[account( + init, + payer = payer, + space = 8 + std::mem::size_of::(), + // │ └─ FastMarketOrderState account data size + // └─ Anchor discriminator (8 bytes) + seeds = [ + FastMarketOrderState::SEED_PREFIX, + vaa_message.digest().as_ref(), + // TODO: consider different seed + ], + bump + )] + fast_market_order: AccountLoader<'info, FastMarketOrderState>, + /// This account should only be created once, and should never be changed to /// init_if_needed. Otherwise someone can game an existing auction. #[account( @@ -145,12 +159,12 @@ impl VaaMessage { u32::from_be_bytes(self.0[4..8].try_into().unwrap()) } - fn emitter_chain(&self) -> u16 { + pub fn emitter_chain(&self) -> u16 { // emitter_chain is the next 2 bytes of the message u16::from_be_bytes(self.0[8..10].try_into().unwrap()) } - fn emitter_address(&self) -> [u8; 32] { + pub fn emitter_address(&self) -> [u8; 32] { // emitter_address is the next 32 bytes of the message self.0[10..42].try_into().unwrap() } @@ -220,18 +234,14 @@ pub fn place_initial_offer_cctp_shim( guardian_set_bump: u8, vaa_message: VaaMessage, ) -> Result<()> { - msg!("Placing initial offer with CCTP shim"); // Extract the guardian set and guardian set signatures accounts from the FastOrderPathShim. let FastOrderPathShim{guardian_set, guardian_set_signatures, live_router_path} = &ctx.accounts.fast_order_path_shim; - msg!("Made fast order path shim"); + // Check that the VAA message corresponds to the accounts in the FastOrderPathShim. let from_endpoint = &live_router_path.from_endpoint; assert_eq!(from_endpoint.chain, vaa_message.emitter_chain()); assert_eq!(from_endpoint.address, vaa_message.emitter_address()); - msg!("Asserted equal emitter chain and address"); - let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message.digest()); - let verify_shim_ix = VerifyHash { program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, accounts: VerifyHashAccounts { @@ -240,16 +250,30 @@ pub fn place_initial_offer_cctp_shim( }, data: verify_hash_data }.instruction(); - msg!("Made verify shim ix"); // Make the cpi call to verify the shim. invoke_signed_unchecked(&verify_shim_ix, &[ guardian_set.to_account_info(), guardian_set_signatures.to_account_info(), ], &[])?; - msg!("Verified shim"); let payload = vaa_message.payload(); let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&payload).unwrap(); + { + let mut fast_market_order = ctx.accounts.fast_market_order.load_init()?; + let redeemer_message: [u8; 512] = order.redeemer_message.to_vec().try_into().unwrap(); + // Set fields directly on the loaded account + fast_market_order.amount_in = order.amount_in; + fast_market_order.min_amount_out = order.min_amount_out; + fast_market_order.target_chain = order.target_chain; + fast_market_order.redeemer = order.redeemer; + fast_market_order.sender = order.sender; + fast_market_order.refund_address = order.refund_address; + fast_market_order.max_fee = order.max_fee; + fast_market_order.init_auction_fee = order.init_auction_fee; + fast_market_order.deadline = order.deadline; + fast_market_order.redeemer_message_length = order.redeemer_message.len() as u16; + fast_market_order.redeemer_message = redeemer_message; + } // fast_market_order is dropped here, releasing the lock // Parse the transfer amount from the VAA. let amount_in = order.amount_in; @@ -291,24 +315,6 @@ pub fn place_initial_offer_cctp_shim( .into(), }); - let info = ctx.accounts.auction.info.as_ref().unwrap(); - - // Emit event for auction participants to listen to. - emit_cpi!(crate::utils::log_emit(crate::events::AuctionUpdated { - config_id: info.config_id, - fast_vaa_hash: ctx.accounts.auction.vaa_hash, - vaa: None, - source_chain: info.source_chain, - target_protocol: ctx.accounts.auction.target_protocol, - redeemer_message_len: info.redeemer_message_len, - end_slot: info.auction_end_slot(config), - best_offer_token: initial_offer_token, - token_balance_before: ctx.accounts.offer_token.amount, - amount_in, - total_deposit: info.total_deposit(), - max_offer_price_allowed: utils::auction::compute_min_allowed_offer(config, info) - .checked_sub(1), - })); // Finally transfer tokens from the offer authority's token account to the // auction's custody account. diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs new file mode 100644 index 000000000..b1660e076 --- /dev/null +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -0,0 +1,49 @@ +use anchor_lang::prelude::*; + +#[account(zero_copy)] +#[derive(Debug)] +#[repr(C)] +pub struct FastMarketOrder { + pub amount_in: u64, + pub min_amount_out: u64, + pub deadline: u32, + pub target_chain: u16, + pub redeemer_message_length: u16, + pub redeemer: [u8; 32], + pub sender: [u8; 32], + pub refund_address: [u8; 32], + pub max_fee: u64, + pub init_auction_fee: u64, + pub redeemer_message: [u8; 512], +} + +impl FastMarketOrder { + pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; + + pub fn to_vec(&self) -> Vec { + let payload_slice = bytemuck::bytes_of(self); + let mut payload = Vec::with_capacity(payload_slice.len()); + payload.extend_from_slice(payload_slice); + payload + } + + pub fn payload(&self) -> Vec { + let mut payload = vec![]; + payload.push(11_u8); + payload.extend_from_slice(&self.amount_in.to_be_bytes()); + payload.extend_from_slice(&self.min_amount_out.to_be_bytes()); + payload.extend_from_slice(&self.target_chain.to_be_bytes()); + payload.extend_from_slice(&self.redeemer); + payload.extend_from_slice(&self.sender); + payload.extend_from_slice(&self.refund_address); + payload.extend_from_slice(&self.max_fee.to_be_bytes()); + payload.extend_from_slice(&self.init_auction_fee.to_be_bytes()); + payload.extend_from_slice(&self.deadline.to_be_bytes()); + payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); + if self.redeemer_message_length > 0 { + payload.extend_from_slice(&self.redeemer_message[..self.redeemer_message_length as usize]); + } + payload + } +} + diff --git a/solana/programs/matching-engine/src/state/mod.rs b/solana/programs/matching-engine/src/state/mod.rs index fbfe98a42..aeea998c6 100644 --- a/solana/programs/matching-engine/src/state/mod.rs +++ b/solana/programs/matching-engine/src/state/mod.rs @@ -19,5 +19,8 @@ pub use prepared_order_response::*; mod proposal; pub use proposal::*; +mod fast_market_order; +pub use fast_market_order::*; + pub(crate) mod router_endpoint; pub use router_endpoint::*; diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 44fab6e04..dd6aa3dfc 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -8,7 +8,9 @@ use utils::initialize::initialize_program; use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; use utils::setup::{PreTestingContext, TestingContext}; use utils::vaa::create_vaas_test_with_chain_and_address; -use utils::shims::{place_initial_offer_shim, set_up_post_message_transaction_test}; +use utils::shims::{ + // place_initial_offer_shim, + place_initial_offer_fallback, set_up_post_message_transaction_test}; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { @@ -155,14 +157,75 @@ pub async fn test_post_message_shims() { } -// TODO: Check that you cannot execute the order the old way and then place the initial offer using the shim +// // TODO: Check that you cannot execute the order the old way and then place the initial offer using the shim +// /// This test should FAIL because of stack overflow issues. +// #[tokio::test] +// pub async fn test_verify_shims() { +// let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); +// pre_testing_context.add_verify_shims(); +// // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. +// let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); +// let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + +// let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); +// let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; +// // TODO: Change the posting of the signatures to be the actual single guardian signature. +// let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; +// let first_test_ft = vaas_test.0.first().unwrap(); +// // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim + +// let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); +// // Try making initial offer using the shim instruction +// let usdc_mint_address = USDC_MINT_ADDRESS; +// let auction_config_address = initialize_fixture.get_auction_config_address(); +// let router_endpoints = create_all_router_endpoints_test( +// &testing_context.test_context, +// testing_context.testing_actors.owner.pubkey(), +// initialize_fixture.get_custodian_address(), +// fixture_accounts.arbitrum_remote_token_messenger, +// fixture_accounts.ethereum_remote_token_messenger, +// usdc_mint_address, +// testing_context.testing_actors.owner.keypair(), +// PROGRAM_ID, +// ).await; +// let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; +// let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + +// let solver = testing_context.testing_actors.solvers[0].clone(); +// let auction_accounts = AuctionAccounts::new( +// None, // Fast VAA pubkey +// solver.clone(), // Solver +// auction_config_address.clone(), // Auction config pubkey +// arb_endpoint_address, // From router endpoint pubkey +// eth_endpoint_address, // To router endpoint pubkey +// initialize_fixture.get_custodian_address(), // Custodian pubkey +// usdc_mint_address, // USDC mint pubkey +// ); + +// let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + + +// let solver = testing_context.testing_actors.solvers[0].clone(); + +// let _initial_offer_fixture = place_initial_offer_shim( +// &testing_context.test_context, +// &testing_context.testing_actors.owner.keypair(), +// &PROGRAM_ID, +// &CORE_BRIDGE_PROGRAM_ID, +// &vaa_data, +// solver, +// &auction_accounts, +// ).await.expect("Failed to place initial offer"); +// } + #[tokio::test] -pub async fn test_verify_shims() { +pub async fn test_verify_shims_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); pre_testing_context.add_verify_shims(); // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; // TODO: Change the posting of the signatures to be the actual single guardian signature. @@ -197,13 +260,13 @@ pub async fn test_verify_shims() { initialize_fixture.get_custodian_address(), // Custodian pubkey usdc_mint_address, // USDC mint pubkey ); - + let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; let solver = testing_context.testing_actors.solvers[0].clone(); - let _initial_offer_fixture = place_initial_offer_shim( + let _initial_offer_fixture = place_initial_offer_fallback( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &PROGRAM_ID, diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index c11c33bcb..1bd123b1d 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -54,7 +54,6 @@ impl AuctionOfferFixture { let auction_account = testing_context.borrow_mut().banks_client.get_account(self.auction_address).await.unwrap().expect("Failed to get auction account"); let mut data_ref = auction_account.data.as_ref(); let auction_account_data : Auction = AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); - println!("Auction account: {:?}", auction_account_data); let auction_info = auction_account_data.info.unwrap(); let expected_auction_info = AuctionInfo { config_id: 0, @@ -108,11 +107,6 @@ pub async fn place_initial_offer( }, }, }; - { - let fast_vaa_account = testing_context.borrow_mut().banks_client.get_account(fast_market_order.vaa_pubkey).await.unwrap().expect("Failed to get fast vaa account"); - println!("Fast VAA Account: {:?}", fast_vaa_account); - println!("fast vaa owner: {:?}", fast_vaa_account.owner); - } let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; let transfer_authority = Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &initial_offer_ix.offer_price.to_be_bytes()], &program_id).0; diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index c5078abb2..d6fcb06ae 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -15,4 +15,5 @@ pub mod vaa; pub mod auction; pub mod setup; pub mod shims; +// pub mod shims_execute_order; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 6b74fafce..975258df7 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -333,6 +333,5 @@ pub async fn create_all_router_endpoints_test( program_id, &usdc_mint_address, ).await; - TestRouterEndpoints::new(arbitrum_token_router_endpoint, ethereum_token_router_endpoint, local_token_router_endpoint) } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 3e2c7099e..d22cd944b 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -1,4 +1,6 @@ use anchor_lang::prelude::*; +use common::messages::FastMarketOrder; +use wormhole_io::TypePrefixedPayload; use super::{constants::*, setup::Solver}; use wormhole_svm_shim::{post_message, verify_vaa}; use solana_sdk::{ @@ -19,9 +21,17 @@ use wormhole_svm_definitions::{ find_shim_message_address, }; use base64::Engine; -use matching_engine::{accounts::{CheckedCustodian, FastOrderPathShim, LiveRouterEndpoint, LiveRouterPath}, state::Auction}; -use matching_engine::instruction::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimIx; -use matching_engine::accounts::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimAccounts; +use matching_engine::{accounts::{CheckedCustodian, + // FastOrderPathShim, + LiveRouterEndpoint, LiveRouterPath}, state::{Auction, FastMarketOrder as FastMarketOrderState}}; +// use matching_engine::instruction::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimIx; +// use matching_engine::accounts::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimAccounts; +use matching_engine::matching_engine::fallback_process_instruction; +use matching_engine::fallback::place_initial_offer::{ + PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, + PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, + PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, +}; use anchor_lang::InstructionData; use solana_sdk::instruction::Instruction; use wormhole_svm_definitions::borsh::GuardianSignatures; @@ -346,9 +356,108 @@ pub struct PlaceInitialOfferShimFixture { } -pub async fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { - // The auction address? is this needed? - let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_data.digest()], &program_id).0; +// pub async fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { +// // The auction address? is this needed? +// let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_data.digest()], &program_id).0; +// let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; +// let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); + +// let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); +// let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); +// let signatures_signer = Rc::new(Keypair::new()); +// let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); + +// let vaa_message = matching_engine::VaaMessage::from_vec(vaa_data.message_vec()); + +// let vaa_digest = vaa_message.digest(); + +// let offer_price: u32 = 1__000_000; +// let place_initial_offer_ix_data = PlaceInitialOfferCctpShimIx { +// offer_price, +// guardian_set_bump, +// vaa_message, +// }.data(); + +// // Approve the transfer authority +// let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; +// solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; +// let checked_custodian = CheckedCustodian { +// custodian: accounts.custodian, +// }; + +// let fast_order_path_shim = FastOrderPathShim { +// guardian_set: guardian_set_pubkey, +// guardian_set_signatures: guardian_signatures_pubkey.clone().to_owned(), +// live_router_path: LiveRouterPath { +// from_endpoint: LiveRouterEndpoint { +// endpoint: accounts.from_router_endpoint, +// }, +// to_endpoint: LiveRouterEndpoint { +// endpoint: accounts.to_router_endpoint, +// }, +// }, +// }; + +// let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; +// let fast_market_order_acc = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, vaa_digest.as_ref()], program_id).0; +// let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimAccounts { +// payer: payer_signer.pubkey(), +// transfer_authority, +// custodian: checked_custodian, +// auction_config: accounts.auction_config, +// fast_order_path_shim, +// fast_market_order: fast_market_order_acc, +// auction: auction_address, +// offer_token: accounts.offer_token, +// auction_custody_token: auction_custody_token_address, +// usdc: matching_engine::accounts::Usdc { mint: accounts.usdc_mint }, +// verify_vaa_shim_program: WORMHOLE_VERIFY_VAA_SHIM_PID, +// system_program: solana_program::system_program::ID, +// token_program: anchor_spl::token::spl_token::ID, +// }; +// let place_initial_offer_ix = Instruction { +// program_id: program_id.clone().to_owned(), +// accounts: place_initial_offer_ix_accounts.to_account_metas(Some(false)), +// data: place_initial_offer_ix_data, +// }; +// let recent_blockhash = test_ctx.borrow().last_blockhash; +// let transaction = Transaction::new_signed_with_payer(&[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); + +// test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to place initial offer"); + +// Ok(PlaceInitialOfferShimFixture { +// auction_address, +// auction_custody_token_address, +// guardian_set_pubkey, +// guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), +// }) +// } + +pub async fn place_initial_offer_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { + + // TODO: Make a new clean PostedVaaData struct from scratch and use that. Make sure nonce is 0. + let vaa_data = super::vaa::PostedVaaData { + consistency_level: vaa_data.consistency_level, + vaa_time: vaa_data.vaa_time, + sequence: vaa_data.sequence, + emitter_chain: vaa_data.emitter_chain, + emitter_address: vaa_data.emitter_address, + payload: vaa_data.payload.clone(), + nonce: 0, + vaa_signature_account: vaa_data.vaa_signature_account, + submission_time: 0, + }; + let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( + vaa_data.consistency_level, + vaa_data.vaa_time, + vaa_data.sequence, + vaa_data.emitter_chain, + vaa_data.emitter_address, + ); + + let vaa_digest = vaa_data.digest(); + + let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_digest], &program_id).0; let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); @@ -356,70 +465,83 @@ pub async fn place_initial_offer_shim(test_ctx: &Rc> let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); let signatures_signer = Rc::new(Keypair::new()); let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); - - let vaa_message = matching_engine::VaaMessage::from_vec(vaa_data.message_vec()); - - println!("Vaa message length: {}", vaa_message.0.len()); - - let offer_price = 1__000_000; - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimIx { - offer_price, - guardian_set_bump, - vaa_message, - }.data(); + let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, vaa_digest.as_ref()], program_id).0; + let offer_price: u64 = 1__000_000; // Approve the transfer authority let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; + solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; - let checked_custodian = CheckedCustodian { - custodian: accounts.custodian, - }; - let fast_order_path_shim = FastOrderPathShim { - guardian_set: guardian_set_pubkey, - guardian_set_signatures: guardian_signatures_pubkey.clone().to_owned(), - live_router_path: LiveRouterPath { - from_endpoint: LiveRouterEndpoint { - endpoint: accounts.from_router_endpoint, - }, - to_endpoint: LiveRouterEndpoint { - endpoint: accounts.to_router_endpoint, - }, - }, + let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); + + let redeemer_message_fixed_length = { + let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) + + if !order.redeemer_message.is_empty() { + // Calculate how many bytes to copy (min of message length and array size) + let copy_len = std::cmp::min(order.redeemer_message.len(), 512); + + // Copy the bytes from the message to the fixed array + fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); + } + + fixed_array + }; + let fast_market_order = FastMarketOrderState { + amount_in: order.amount_in, + min_amount_out: order.min_amount_out, + deadline: order.deadline, + target_chain: order.target_chain, + redeemer_message_length: order.redeemer_message.len() as u16, + redeemer: order.redeemer, + sender: order.sender, + refund_address: order.refund_address, + max_fee: order.max_fee, + init_auction_fee: order.init_auction_fee, + redeemer_message: redeemer_message_fixed_length, }; - let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; - - let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimAccounts { - payer: payer_signer.pubkey(), - transfer_authority, - custodian: checked_custodian, - auction_config: accounts.auction_config, - fast_order_path_shim, - auction: auction_address, - offer_token: accounts.offer_token, - auction_custody_token: auction_custody_token_address, - usdc: matching_engine::accounts::Usdc { mint: accounts.usdc_mint }, - verify_vaa_shim_program: WORMHOLE_VERIFY_VAA_SHIM_PID, - system_program: solana_program::system_program::ID, - token_program: anchor_spl::token::spl_token::ID, - event_authority, - program: program_id.clone().to_owned(), + assert_eq!(fast_market_order.redeemer, order.redeemer); + assert_eq!(vaa_message.digest(&fast_market_order).as_ref(), vaa_data.digest().as_ref()); + + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price, vaa_data.sequence, vaa_data.vaa_time, guardian_set_bump, vaa_data.consistency_level, fast_market_order); + + let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { + signer: &payer_signer.pubkey(), + transfer_authority: &transfer_authority, + custodian: &accounts.custodian, + auction_config: &accounts.auction_config, + guardian_set: &guardian_set_pubkey, + guardian_set_signatures: &guardian_signatures_pubkey, + from_endpoint: &accounts.from_router_endpoint, + to_endpoint: &accounts.to_router_endpoint, + fast_market_order: &fast_market_order_account, + auction: &auction_address, + offer_token: &accounts.offer_token, + auction_custody_token: &auction_custody_token_address, + usdc: &accounts.usdc_mint, + verify_vaa_shim_program: &WORMHOLE_VERIFY_VAA_SHIM_PID, + system_program: &solana_program::system_program::ID, + token_program: &anchor_spl::token::spl_token::ID, }; - let place_initial_offer_ix = Instruction { - program_id: program_id.clone().to_owned(), - accounts: place_initial_offer_ix_accounts.to_account_metas(Some(false)), + let place_initial_offer_ix = PlaceInitialOfferCctpShimFallback { + program_id: program_id, + accounts: place_initial_offer_ix_accounts, data: place_initial_offer_ix_data, - }; + }.instruction(); + let recent_blockhash = test_ctx.borrow().last_blockhash; + let transaction = Transaction::new_signed_with_payer(&[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to place initial offer"); + Ok(PlaceInitialOfferShimFixture { auction_address, auction_custody_token_address, guardian_set_pubkey, guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), }) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs new file mode 100644 index 000000000..7d85a488a --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs @@ -0,0 +1,28 @@ +use anchor_lang::prelude::*; +use super::{constants::*, setup::Solver}; +use wormhole_svm_shim::{post_message, verify_vaa}; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + hash::Hash, + message::{v0::Message, VersionedMessage}, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, + transaction::{Transaction, VersionedTransaction}, +}; +use solana_program_test::ProgramTestContext; +use std::{rc::Rc, str::FromStr}; +use std::cell::RefCell; +use wormhole_svm_definitions::{ + solana::Finality, + find_emitter_sequence_address, + find_shim_message_address, +}; +use base64::Engine; +use matching_engine::{accounts::{CheckedCustodian, FastOrderPathShim, LiveRouterEndpoint, LiveRouterPath}, state::Auction}; +use matching_engine::instruction::ExecuteFastOrderCctp as ExecuteFastOrderCctpIx; +use matching_engine::accounts::ExecuteFastOrderCctp as ExecuteFastOrderCctpAccounts; +use anchor_lang::InstructionData; +use solana_sdk::instruction::Instruction; +use wormhole_svm_definitions::borsh::GuardianSignatures; + From fa677eea48b62ec696354981afc3f30ff365ac02 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 27 Feb 2025 18:45:08 +0000 Subject: [PATCH 012/112] up to date with 21b4eae5 of shims --- .../fallback/processor/place_initial_offer.rs | 25 +-- .../auction/offer/place_initial/cctp_shim.rs | 145 +----------------- 2 files changed, 6 insertions(+), 164 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index d01b6134f..a36499a2b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,15 +1,13 @@ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use bytemuck::{Pod, Zeroable}; -use common::messages::FastMarketOrder; use solana_program::instruction::Instruction; use solana_program::program::invoke_signed_unchecked; use super::create_account::create_account_reliably; // TODO: Use this everywhere (but note that there is a bug with index zero on cpi) use solana_program::keccak; use anchor_lang::Discriminator; use solana_program::program_pack::Pack; -use wormhole_io::TypePrefixedPayload; -use wormhole_svm_shim::verify_vaa::{GuardianSetPubkey, VerifyHash, VerifyHashAccounts, VerifyHashData}; +use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashAccounts, VerifyHashData}; use crate::state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use crate::ID as PROGRAM_ID; @@ -314,22 +312,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti } } let fast_market_order_key = fast_market_order_account.key(); - let redeemer = fast_market_order_zero_copy.redeemer; - let sender = fast_market_order_zero_copy.sender; - let refund_address = fast_market_order_zero_copy.refund_address; - let payload = FastMarketOrder { - amount_in: fast_market_order_zero_copy.amount_in, - min_amount_out: fast_market_order_zero_copy.min_amount_out, - target_chain: fast_market_order_zero_copy.target_chain, - redeemer, - sender, - refund_address, - max_fee: fast_market_order_zero_copy.max_fee, - init_auction_fee: fast_market_order_zero_copy.init_auction_fee, - deadline: fast_market_order_zero_copy.deadline, - redeemer_message: fast_market_order_zero_copy.redeemer_message[..fast_market_order_zero_copy.redeemer_message_length as usize].to_vec().try_into().unwrap(), - }.to_vec(); // Create the vaa_message struct to get the digest let vaa_message = VaaMessageBodyHeader::new(consistency_level, vaa_time, sequence, from_endpoint_account.chain, from_endpoint_account.address); let vaa_message_digest = vaa_message.digest(&fast_market_order_zero_copy); @@ -337,9 +320,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti // Begin of initialisation of auction custody token account // ------------------------------------------------------------------------------------------------ let auction_custody_token_space = spl_token::state::Account::LEN; - let rent = Rent::get()?; - let lamports = rent.minimum_balance(auction_custody_token_space); - + let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address(&[crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_key.as_ref()], &program_id); if auction_custody_token_pda != auction_custody_token.key() { msg!("Auction custody token pda is invalid. Passed account: {}, expected: {}", auction_custody_token.key(), auction_custody_token_pda); @@ -507,7 +488,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti let verify_shim_ix = VerifyHash { program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, accounts: VerifyHashAccounts { - guardian_set: GuardianSetPubkey::Provided(&guardian_set.key()), + guardian_set: &guardian_set.key(), guardian_signatures: &guardian_set_signatures.key(), }, data: verify_hash_data diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index 332d2fe1b..d7c73a4f8 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -1,15 +1,12 @@ use crate::{ composite::*, error::MatchingEngineError, - state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, FastMarketOrder as FastMarketOrderState}, - utils, + state::{Auction, AuctionConfig, FastMarketOrder as FastMarketOrderState}, }; use anchor_lang::prelude::*; use anchor_spl::token; -use common::{messages::FastMarketOrder, TRANSFER_AUTHORITY_SEED_PREFIX}; -use wormhole_svm_shim::verify_vaa::{GuardianSetPubkey, VerifyHash, VerifyHashAccounts, VerifyHashData}; -use common::wormhole_io::TypePrefixedPayload; -use solana_program::{keccak, program::invoke_signed_unchecked}; +use common::TRANSFER_AUTHORITY_SEED_PREFIX; +use solana_program::keccak; #[derive(Accounts)] @@ -134,25 +131,6 @@ impl VaaMessage { keccak::hashv(&[self.message_hash().as_ref()]) } - fn payload(&self) -> Vec { - // Calculate offset: - // vaa_time (u32) = 4 bytes - // nonce (u32) = 4 bytes - // emitter_chain (u16) = 2 bytes - // emitter_address = 32 bytes - // sequence (u64) = 8 bytes - // consistency_level (u8) = 1 byte - // Total offset = 51 bytes - - // Everything after the offset is the payload - self.0[51..].to_vec() - } - - fn vaa_time(&self) -> u32 { - // vaa_time is the first 4 bytes of the message - u32::from_be_bytes(self.0[0..4].try_into().unwrap()) - } - #[allow(dead_code)] fn nonce(&self) -> u32 { // nonce is the next 4 bytes of the message @@ -169,12 +147,6 @@ impl VaaMessage { self.0[10..42].try_into().unwrap() } - fn sequence(&self) -> u64 { - // sequence is the next 8 bytes of the message - u64::from_be_bytes(self.0[42..50].try_into().unwrap()) - } - - } /// Just a helper struct to make the code more readable. @@ -226,115 +198,4 @@ impl VaaMessageBody { self.payload.as_ref(), ].concat() } -} - -pub fn place_initial_offer_cctp_shim( - ctx: Context, - offer_price: u64, - guardian_set_bump: u8, - vaa_message: VaaMessage, -) -> Result<()> { - // Extract the guardian set and guardian set signatures accounts from the FastOrderPathShim. - let FastOrderPathShim{guardian_set, guardian_set_signatures, live_router_path} = &ctx.accounts.fast_order_path_shim; - - // Check that the VAA message corresponds to the accounts in the FastOrderPathShim. - let from_endpoint = &live_router_path.from_endpoint; - assert_eq!(from_endpoint.chain, vaa_message.emitter_chain()); - assert_eq!(from_endpoint.address, vaa_message.emitter_address()); - let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message.digest()); - let verify_shim_ix = VerifyHash { - program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: VerifyHashAccounts { - guardian_set: GuardianSetPubkey::Provided(&guardian_set.key()), - guardian_signatures: &guardian_set_signatures.key(), - }, - data: verify_hash_data - }.instruction(); - // Make the cpi call to verify the shim. - invoke_signed_unchecked(&verify_shim_ix, &[ - guardian_set.to_account_info(), - guardian_set_signatures.to_account_info(), - ], &[])?; - let payload = vaa_message.payload(); - - let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&payload).unwrap(); - { - let mut fast_market_order = ctx.accounts.fast_market_order.load_init()?; - let redeemer_message: [u8; 512] = order.redeemer_message.to_vec().try_into().unwrap(); - // Set fields directly on the loaded account - fast_market_order.amount_in = order.amount_in; - fast_market_order.min_amount_out = order.min_amount_out; - fast_market_order.target_chain = order.target_chain; - fast_market_order.redeemer = order.redeemer; - fast_market_order.sender = order.sender; - fast_market_order.refund_address = order.refund_address; - fast_market_order.max_fee = order.max_fee; - fast_market_order.init_auction_fee = order.init_auction_fee; - fast_market_order.deadline = order.deadline; - fast_market_order.redeemer_message_length = order.redeemer_message.len() as u16; - fast_market_order.redeemer_message = redeemer_message; - } // fast_market_order is dropped here, releasing the lock - - // Parse the transfer amount from the VAA. - let amount_in = order.amount_in; - - // Saturating to u64::MAX is safe here. If the amount really ends up being this large, the - // checked addition below will catch it. - let security_deposit = - order - .max_fee - .saturating_add(utils::auction::compute_notional_security_deposit( - &ctx.accounts.auction_config, - amount_in, - )); - - // Set up the Auction account for this auction. - let config = &ctx.accounts.auction_config; - let initial_offer_token = ctx.accounts.offer_token.key(); - ctx.accounts.auction.set_inner(Auction { - bump: ctx.bumps.auction, - vaa_hash: vaa_message.digest().as_ref().try_into().unwrap(), - vaa_timestamp: vaa_message.vaa_time(), - target_protocol: live_router_path.to_endpoint.protocol, - status: AuctionStatus::Active, - prepared_by: ctx.accounts.payer.key(), - info: AuctionInfo { - config_id: config.id, - custody_token_bump: ctx.bumps.auction_custody_token, - vaa_sequence: vaa_message.sequence(), - source_chain: vaa_message.emitter_chain(), - best_offer_token: initial_offer_token, - initial_offer_token, - start_slot: Clock::get().unwrap().slot, - amount_in, - security_deposit, - offer_price, - redeemer_message_len: order.redeemer_message.len() as u16, - destination_asset_info: Default::default(), - } - .into(), - }); - - - // Finally transfer tokens from the offer authority's token account to the - // auction's custody account. - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - anchor_spl::token::Transfer { - from: ctx.accounts.offer_token.to_account_info(), - to: ctx.accounts.auction_custody_token.to_account_info(), - authority: ctx.accounts.transfer_authority.to_account_info(), - }, - &[&[ - TRANSFER_AUTHORITY_SEED_PREFIX, - ctx.accounts.auction.key().as_ref(), - &offer_price.to_be_bytes(), - &[ctx.bumps.transfer_authority], - ]], - ), - amount_in - .checked_add(security_deposit) - .ok_or_else(|| MatchingEngineError::U64Overflow)?, - ) } \ No newline at end of file From ec90de5edbdff170d1ba5529c9e6712713d6d270 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 3 Mar 2025 21:43:04 +0000 Subject: [PATCH 013/112] [broken]: written execute order test but it is failing --- .../src/fallback/processor/burn_and_post.rs | 85 +++ .../src/fallback/processor/execute_order.rs | 552 ++++++++++++++++++ .../src/fallback/processor/mod.rs | 6 +- .../fallback/processor/place_initial_offer.rs | 4 +- .../fallback/processor/process_instruction.rs | 21 + .../tests/initialize_integration_tests.rs | 156 ++--- .../matching-engine/tests/utils/mod.rs | 2 +- .../matching-engine/tests/utils/shims.rs | 108 +--- .../tests/utils/shims_execute_order.rs | 122 +++- .../matching-engine/tests/utils/vaa.rs | 52 +- 10 files changed, 897 insertions(+), 211 deletions(-) create mode 100644 solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/execute_order.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs new file mode 100644 index 000000000..f74b24256 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -0,0 +1,85 @@ +use common::wormhole_cctp_solana::{cctp::token_messenger_minter_program::cpi::{DepositForBurnWithCallerParams, DepositForBurnWithCaller, deposit_for_burn_with_caller}, cpi::BurnAndPublishArgs}; +use solana_program::program::invoke_signed_unchecked; +use wormhole_svm_shim::post_message; +use wormhole_svm_definitions::solana::{CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID}; +use anchor_lang::prelude::*; +use wormhole_svm_definitions::{solana::Finality, find_emitter_sequence_address, find_shim_message_address}; + +// This is a helper struct to make it easier to pass in the accounts for the post_message instruction. +pub struct PostMessageAccounts { + pub emitter: Pubkey, + pub payer: Pubkey, + derived: PostMessageDerivedAccounts, +} + +impl PostMessageAccounts { + pub fn new(emitter: Pubkey, payer: Pubkey) -> Self { + Self { + emitter, + payer, + derived: Self::get_derived_accounts(&emitter), + } + } + fn get_derived_accounts(emitter: &Pubkey) -> PostMessageDerivedAccounts { + PostMessageDerivedAccounts { + message: find_shim_message_address(emitter, &POST_MESSAGE_SHIM_PROGRAM_ID).0, + sequence: find_emitter_sequence_address(emitter, &CORE_BRIDGE_PROGRAM_ID).0, + } + } +} + +pub struct PostMessageDerivedAccounts { + pub message: Pubkey, + pub sequence: Pubkey, +} + +pub fn burn_and_post<'info>( + cctp_ctx: CpiContext<'_, '_, '_, 'info, DepositForBurnWithCaller<'info>>, + burn_and_publish_args: BurnAndPublishArgs, post_message_accounts: PostMessageAccounts, + account_infos: &[AccountInfo]) -> Result<()> { + let BurnAndPublishArgs { + burn_source: _, + destination_caller, + destination_cctp_domain, + amount, + mint_recipient, + wormhole_message_nonce, + payload, + } = burn_and_publish_args; + + let post_message_ix = post_message::PostMessage { + program_id: &POST_MESSAGE_SHIM_PROGRAM_ID, + accounts: post_message::PostMessageAccounts { + emitter: &post_message_accounts.emitter, + payer: &post_message_accounts.payer, + wormhole_program_id: &CORE_BRIDGE_PROGRAM_ID, + derived: post_message::PostMessageDerivedAccounts { + message: Some(&post_message_accounts.derived.message), + sequence: Some(&post_message_accounts.derived.sequence), + core_bridge_config: Some(&CORE_BRIDGE_CONFIG), + fee_collector: Some(&CORE_BRIDGE_FEE_COLLECTOR), + event_authority: Some(&POST_MESSAGE_SHIM_EVENT_AUTHORITY), + }, + }, + data: post_message::PostMessageData::new( + wormhole_message_nonce, + Finality::Finalized, + &payload, + ) + .unwrap(), + } + .instruction(); + + invoke_signed_unchecked(&post_message_ix, account_infos, &[])?; + + deposit_for_burn_with_caller( + cctp_ctx, + DepositForBurnWithCallerParams { + amount, + destination_domain: destination_cctp_domain, + mint_recipient, + destination_caller, + }, + )?; + Ok(()) +} diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs new file mode 100644 index 000000000..6ac49015a --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -0,0 +1,552 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; +use bytemuck::{Pod, Zeroable}; +use common::messages::Fill; +use crate::utils; +use solana_program::instruction::Instruction; +use crate::state::{Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; +use crate::utils::auction::DepositPenalty; +use common::wormhole_io::TypePrefixedPayload; + +use super::burn_and_post::{PostMessageAccounts, burn_and_post}; +use super::FallbackMatchingEngineInstruction; +use super::errors::FallbackError; +use crate::error::MatchingEngineError; + + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct ExecuteOrderCctpShimData { + pub destination_cctp_domain: u32, + _padding: [u8; 4], +} + +impl ExecuteOrderCctpShimData { + pub fn new(destination_cctp_domain: u32) -> Self { + Self { + destination_cctp_domain, + _padding: [0; 4], + } + } + + pub fn from_bytes(data: &[u8]) -> Option<&Self> { + bytemuck::try_from_bytes::(data).ok() + } +} +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub struct ExecuteOrderShimAccounts<'ix> { + pub signer: &'ix Pubkey, // 0 + pub cctp_message: &'ix Pubkey, // 2 + pub custodian: &'ix Pubkey, // 3 + pub fast_market_order: &'ix Pubkey, // 4 + pub active_auction: &'ix Pubkey, // 5 + pub active_auction_custody_token: &'ix Pubkey, // 6 + pub active_auction_config: &'ix Pubkey, // 7 + pub active_auction_best_offer_token: &'ix Pubkey, // 8 + pub executor_token: &'ix Pubkey, // 9 + pub initial_offer_token: &'ix Pubkey, // 10 + pub initial_participant: &'ix Pubkey, // 11 + pub to_router_endpoint: &'ix Pubkey, // 12 + // Add shim post message accounts. TODO: Ask about if these are needed at all, or if they can just be imported/derived + pub post_message_shim_program: &'ix Pubkey, // 13 + pub post_message_sequence: &'ix Pubkey, // 14 + pub post_message_message: &'ix Pubkey, // 15 + pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 16 + pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 17 + pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 18 + pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 19 + pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 20 + pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 21 + pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 22 + pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 23 + pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 24 + pub cctp_deposit_for_burn_message_transmitter_program: &'ix Pubkey, // 25 + pub system_program: &'ix Pubkey, // 26 + pub token_program: &'ix Pubkey, // 27 + pub clock: &'ix Pubkey, // 28 + pub rent: &'ix Pubkey, // 29 +} + +impl<'ix> ExecuteOrderShimAccounts<'ix> { + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new(*self.signer, true), + AccountMeta::new(*self.cctp_message, false), + AccountMeta::new(*self.custodian, false), + AccountMeta::new(*self.fast_market_order, false), + AccountMeta::new(*self.active_auction, false), + AccountMeta::new(*self.active_auction_custody_token, false), + AccountMeta::new(*self.active_auction_config, false), + AccountMeta::new(*self.active_auction_best_offer_token, false), + AccountMeta::new(*self.executor_token, false), + AccountMeta::new(*self.initial_offer_token, false), + AccountMeta::new(*self.initial_participant, false), + AccountMeta::new(*self.to_router_endpoint, false), + AccountMeta::new(*self.post_message_shim_program, false), + AccountMeta::new(*self.post_message_sequence, false), + AccountMeta::new(*self.post_message_message, false), + AccountMeta::new(*self.cctp_deposit_for_burn_mint, false), + AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, false), + AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_config, false), + AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger, false), + AccountMeta::new(*self.cctp_deposit_for_burn_remote_token_messenger, false), + AccountMeta::new(*self.cctp_deposit_for_burn_token_minter, false), + AccountMeta::new(*self.cctp_deposit_for_burn_local_token, false), + AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_event_authority, false), + AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_program, false), + AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_program, false), + AccountMeta::new(*self.post_message_shim_program, false), + AccountMeta::new(*self.system_program, false), + AccountMeta::new(*self.token_program, false), + AccountMeta::new(*self.clock, false), + AccountMeta::new(*self.rent, false), + ] + } +} + +pub struct ExecuteOrderCctpShim<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: ExecuteOrderShimAccounts<'ix>, + pub data: ExecuteOrderCctpShimData, +} + +impl ExecuteOrderCctpShim<'_> { + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: FallbackMatchingEngineInstruction::ExecuteOrderCctpShim(&self.data).to_vec(), + } + } +} +pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCctpShimData) -> Result<()> { + let program_id = &crate::ID; + + // Get the accounts + let signer_account = &accounts[0]; + let cctp_message_account = &accounts[2]; + let custodian_account = &accounts[3]; + let fast_market_order_account = &accounts[4]; + let active_auction_account = &accounts[5]; + let active_auction_custody_token_account = &accounts[6]; + let active_auction_config_account = &accounts[7]; + let active_auction_best_offer_token_account = &accounts[8]; + let executor_token_account = &accounts[9]; + let initial_offer_token_account = &accounts[10]; + let initial_participant_account = &accounts[11]; + let to_router_endpoint_account = &accounts[12]; + // TODO: These are not used, so can I just ignore them? + let post_message_shim_program_account = &accounts[13]; + let post_message_sequence_account = &accounts[14]; + let post_message_message_account = &accounts[15]; + let cctp_deposit_for_burn_mint_account = &accounts[16]; + let cctp_deposit_for_burn_token_messenger_minter_sender_authority_account = &accounts[17]; + let cctp_deposit_for_burn_message_transmitter_config_account = &accounts[18]; + let cctp_deposit_for_burn_token_messenger_account = &accounts[19]; + let cctp_deposit_for_burn_remote_token_messenger_account = &accounts[20]; + let cctp_deposit_for_burn_token_minter_account = &accounts[21]; + let cctp_deposit_for_burn_local_token_account = &accounts[22]; + let cctp_deposit_for_burn_token_messenger_minter_event_authority_account = &accounts[23]; + let cctp_deposit_for_burn_token_messenger_minter_program_account = &accounts[24]; + let cctp_deposit_for_burn_message_transmitter_program_account = &accounts[25]; + let system_program_account = &accounts[26]; + let token_program_account = &accounts[27]; + let _clock_account = &accounts[28]; + let _rent_account = &accounts[29]; + + let ExecuteOrderCctpShimData { + destination_cctp_domain, + _padding, + } = data; + + // Do checks + // ------------------------------------------------------------------------------------------------ + + // Bind value for compiler (needed for pda seeds) + let active_auction_key = active_auction_account.key(); + + // Check cctp message is mutable + if !cctp_message_account.is_writable { + msg!("Cctp message is not writable"); + return Err(FallbackError::AccountNotWritable.into()) + .map_err(|e: Error| e.with_account_name("cctp_message")); + } + + // Check cctp message seeds + let cctp_message_seeds = [ + common::CCTP_MESSAGE_SEED_PREFIX, + active_auction_key.as_ref(), + ]; + let (cctp_message_pda, cctp_message_bump) = Pubkey::find_program_address(&cctp_message_seeds, program_id); + if cctp_message_pda != cctp_message_account.key() { + msg!("Cctp message seeds are invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((cctp_message_pda, cctp_message_account.key()))); + }; + + // Check custodian owner + if custodian_account.owner != program_id { + msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian_account.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + }; + + // Check custodian is not paused + let checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; + if checked_custodian.paused { + msg!("Custodian is paused"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + }; + + // Check fast market order seeds + let fast_market_order_seeds = [ + FastMarketOrderState::SEED_PREFIX, + active_auction_key.as_ref(), + ]; + let (fast_market_order_pda, _fast_market_order_bump) = Pubkey::find_program_address(&fast_market_order_seeds, program_id); + if fast_market_order_pda != fast_market_order_account.key() { + msg!("Fast market order seeds are invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((fast_market_order_pda, fast_market_order_account.key()))); + }; + + // Check fast market order is owned by the matching engine program + if fast_market_order_account.owner != program_id { + msg!("Fast market order is not owned by the matching engine program"); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + }; + + // Check active auction owner + if active_auction_account.owner != program_id { + msg!("Active auction is not owned by the matching engine program"); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("active_auction")); + }; + + // Check active auction pda + let mut active_auction = Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; + + // Correct way to use create_program_address with existing seeds and bump + let active_auction_pda = Pubkey::create_program_address( + &[ + Auction::SEED_PREFIX, + active_auction.vaa_hash.as_ref(), + &[active_auction.bump], + ], + program_id + ).map_err(|_| { + msg!("Failed to create program address with known bump"); + FallbackError::InvalidPda + })?; + if active_auction_pda != active_auction_account.key() { + msg!("Active auction pda is invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((active_auction_pda, active_auction_account.key()))); + }; + + // Check active auction is active + if active_auction.status != AuctionStatus::Active { + msg!("Active auction is not active"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("active_auction")); + }; + + // Check active auction custody token pda + let active_auction_custody_token_pda = Pubkey::create_program_address( + &[ + crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + active_auction_account.key().as_ref(), + &[active_auction.info.as_ref().unwrap().custody_token_bump], + ], + program_id + ).map_err(|_| { + msg!("Failed to create program address with known bump"); + FallbackError::InvalidPda + })?; + if active_auction_custody_token_pda != active_auction_custody_token_account.key() { + msg!("Active auction custody token pda is invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((active_auction_custody_token_pda, active_auction_custody_token_account.key()))); + }; + + // Check active auction config id + let active_auction_config = AuctionConfig::try_deserialize(&mut &active_auction_config_account.data.borrow()[..])?; + if active_auction_config.id != active_auction.info.as_ref().unwrap().config_id { + msg!("Active auction config id is invalid"); + return Err(MatchingEngineError::AuctionConfigMismatch.into()) + .map_err(|e: Error| e.with_account_name("active_auction_config")); + }; + + // Check active auction best offer token address + if active_auction_best_offer_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { + msg!("Active auction best offer token address is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((active_auction_best_offer_token_account.key(), active_auction.info.as_ref().unwrap().best_offer_token))); + }; + + // TODO: Done with auction checks, now on to executor token checks + let executor_token = anchor_spl::token::TokenAccount::try_deserialize(&mut &executor_token_account.data.borrow()[..])?; + if executor_token.mint != active_auction.info.as_ref().unwrap().best_offer_token { + msg!("Executor token mint is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((executor_token.mint, active_auction.info.as_ref().unwrap().best_offer_token))); + }; + + // Check initial offer token address + if initial_offer_token_account.key() != active_auction.info.as_ref().unwrap().initial_offer_token { + msg!("Initial offer token address is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((initial_offer_token_account.key(), active_auction.info.as_ref().unwrap().initial_offer_token))); + }; + + // Check initial participant address + if initial_participant_account.key() != active_auction.prepared_by { + msg!("Initial participant address is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((initial_participant_account.key(), active_auction.prepared_by))); + }; + + let to_router_endpoint = RouterEndpoint::try_deserialize(&mut &to_router_endpoint_account.data.borrow()[..])?; + if to_router_endpoint.protocol != active_auction.target_protocol { + msg!("To router endpoint protocol is invalid"); + return Err(MatchingEngineError::InvalidEndpoint.into()) + .map_err(|e: Error| e.with_account_name("to_router_endpoint")); + }; + + // TODO: Ask about seeds for wormhole publish message accounts + + // Check cctp deposit for burn token messenger minter program address + if cctp_deposit_for_burn_token_messenger_minter_program_account.key() != common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id() { + msg!("Cctp deposit for burn token messenger minter program address is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((cctp_deposit_for_burn_token_messenger_minter_program_account.key(), common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id()))); + }; + + // Check cctp deposit for burn message transmitter program address + if cctp_deposit_for_burn_message_transmitter_program_account.key() != common::wormhole_cctp_solana::cctp::message_transmitter_program::id() { + msg!("Cctp deposit for burn message transmitter program address is invalid"); + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_pubkeys((cctp_deposit_for_burn_message_transmitter_program_account.key(), common::wormhole_cctp_solana::cctp::message_transmitter_program::id()))); + }; + + // End of checks + // ------------------------------------------------------------------------------------------------ + + let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; + // Deserialise fast market order. Unwrap is safe because the account is owned by the matching engine program. + let fast_market_order = bytemuck::try_from_bytes::( + &fast_market_order_data[..] + ) + .unwrap(); + + // Prepare the execute order (get the user amount, fill, and order executed event) + let active_auction_info = active_auction.info.as_ref().unwrap(); + let current_slot = Clock::get().unwrap().slot; + + // We extend the grace period for locally executed orders. Reserving a sequence number for + // the fast fill will most likely require an additional transaction, so this buffer allows + // the best offer participant to perform his duty without the risk of getting slashed by + // another executor. + let additional_grace_period = match active_auction.target_protocol { + MessageProtocol::Local { .. } => { + crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD.into() + } + _ => None, + }; + + let DepositPenalty { + penalty, + user_reward, + } = utils::auction::compute_deposit_penalty( + &active_auction_config.parameters, + active_auction_info, + current_slot, + additional_grace_period, + ); + + let init_auction_fee = fast_market_order.init_auction_fee; + + let user_amount = active_auction_info + .amount_in + .saturating_sub(active_auction_info.offer_price) + .saturating_sub(init_auction_fee) + .saturating_add(user_reward); + + // Keep track of the remaining amount in the custody token account. Whatever remains will go + // to the executor. + let mut remaining_custodied_amount = active_auction_info.amount_in.saturating_sub(user_amount); + + // Offer price + security deposit was checked in placing the initial offer. + let mut deposit_and_fee = active_auction_info + .offer_price + .saturating_add(active_auction_info.security_deposit) + .saturating_sub(user_reward); + + let penalized = penalty > 0; + + if penalized && active_auction_best_offer_token_account.key() != executor_token_account.key() { + deposit_and_fee = deposit_and_fee.saturating_sub(penalty); + } + + // If the initial offer token account doesn't exist anymore, we have nowhere to send the + // init auction fee. The executor will get these funds instead. + // + // We check that this is a legitimate token account. + if utils::checked_deserialize_token_account(initial_offer_token_account, &common::USDC_MINT) + .is_some() + { + if active_auction_best_offer_token_account.key() != initial_offer_token_account.key() { + // Pay the auction initiator their fee. + spl_token::instruction::transfer( + &spl_token::ID, + &active_auction_custody_token_account.key(), + &initial_offer_token_account.key(), + &active_auction_account.key(), + &[], + init_auction_fee, + ).unwrap(); + + // Because the initial offer token was paid this fee, we account for it here. + remaining_custodied_amount = + remaining_custodied_amount.saturating_sub(init_auction_fee); + } else { + // Add it to the reimbursement. + deposit_and_fee = deposit_and_fee + .checked_add(init_auction_fee) + .ok_or_else(|| MatchingEngineError::U64Overflow)?; + } + } + + // Return the security deposit and the fee to the highest bidder. + // + if active_auction_best_offer_token_account.key() == executor_token_account.key() { + // If the best offer token is equal to the executor token, just send whatever remains in + // the custody token account. + // + // NOTE: This will revert if the best offer token does not exist. But this will present + // an opportunity for another executor to execute this order and take what the best + // offer token would have received. + spl_token::instruction::transfer( + &spl_token::ID, + &active_auction_custody_token_account.key(), + &active_auction_best_offer_token_account.key(), + &active_auction_account.key(), + &[], + remaining_custodied_amount, + ).unwrap(); + } else { + // Otherwise, send the deposit and fee to the best offer token. If the best offer token + // doesn't exist at this point (which would be unusual), we will reserve these funds + // for the executor token. + if utils::checked_deserialize_token_account(active_auction_best_offer_token_account, &common::USDC_MINT) + .is_some() + { + spl_token::instruction::transfer( + &spl_token::ID, + &active_auction_custody_token_account.key(), + &active_auction_best_offer_token_account.key(), + &active_auction_account.key(), + &[], + deposit_and_fee, + ).unwrap(); + remaining_custodied_amount = + remaining_custodied_amount.saturating_sub(deposit_and_fee); + } + + // And pay the executor whatever remains in the auction custody token account. + if remaining_custodied_amount > 0 { + spl_token::instruction::transfer( + &spl_token::ID, + &active_auction_custody_token_account.key(), + &executor_token_account.key(), + &active_auction_account.key(), + &[], + remaining_custodied_amount, + ).unwrap(); + } + } + + // Set the authority of the custody token account to the custodian. He will take over from + // here. + spl_token::instruction::set_authority( + &spl_token::ID, + &active_auction_custody_token_account.key(), + Some(&custodian_account.key()), + spl_token::instruction::AuthorityType::AccountOwner, + &active_auction_account.key(), + &[], + ).unwrap(); + + // Set the active auction status + active_auction.status = AuctionStatus::Completed { + slot: current_slot, + execute_penalty: if penalized { penalty.into() } else { None }, + }; + + let fill = Fill { + source_chain: active_auction_info.source_chain, + order_sender: fast_market_order.sender, + redeemer: fast_market_order.redeemer, + redeemer_message: fast_market_order.redeemer_message[..fast_market_order.redeemer_message_length as usize].to_vec().try_into().unwrap(), + }; + + let post_message_accounts = PostMessageAccounts::new(custodian_account.key(), signer_account.key()); + + burn_and_post( + CpiContext::new_with_signer( + cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), + common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { + burn_token_owner: custodian_account.to_account_info(), + payer: signer_account.to_account_info(), + token_messenger_minter_sender_authority: cctp_deposit_for_burn_token_messenger_minter_sender_authority_account.to_account_info(), + burn_token: active_auction_custody_token_account.to_account_info(), + message_transmitter_config: cctp_deposit_for_burn_message_transmitter_config_account.to_account_info(), + token_messenger: cctp_deposit_for_burn_token_messenger_account.to_account_info(), + remote_token_messenger: cctp_deposit_for_burn_remote_token_messenger_account.to_account_info(), + token_minter: cctp_deposit_for_burn_token_minter_account.to_account_info(), + local_token: cctp_deposit_for_burn_local_token_account.to_account_info(), + mint: cctp_deposit_for_burn_mint_account.to_account_info(), + cctp_message: cctp_message_account.to_account_info(), + message_transmitter_program: cctp_deposit_for_burn_message_transmitter_program_account.to_account_info(), + token_messenger_minter_program: cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), + token_program: token_program_account.to_account_info(), + system_program: system_program_account.to_account_info(), + event_authority: cctp_deposit_for_burn_token_messenger_minter_event_authority_account.to_account_info(), + }, + &[ + Custodian::SIGNER_SEEDS, + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + active_auction_account.key().as_ref(), + &[cctp_message_bump], + ], + ], + ), + common::wormhole_cctp_solana::cpi::BurnAndPublishArgs { + burn_source: None, + destination_caller: to_router_endpoint.address, + destination_cctp_domain: *destination_cctp_domain, + amount: user_amount, + mint_recipient: to_router_endpoint.mint_recipient, + wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, + payload: fill.to_vec(), + }, + post_message_accounts, + accounts + )?; + + // Skip emitting the order executed event because we're using a shim + + // Finally close the account since it is no longer needed. + spl_token::instruction::close_account( + &spl_token::ID, + &active_auction_custody_token_account.key(), + &initial_participant_account.key(), + &custodian_account.key(), + &[], + ).unwrap(); + + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 3eb9bfd82..22ab72212 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,5 +1,7 @@ pub mod process_instruction; pub use process_instruction::*; -pub mod place_initial_offer; pub mod errors; -pub mod create_account; \ No newline at end of file +pub mod create_account; +pub mod place_initial_offer; +pub mod execute_order; +pub mod burn_and_post; \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index a36499a2b..0adb54505 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -3,7 +3,7 @@ use anchor_spl::token::spl_token; use bytemuck::{Pod, Zeroable}; use solana_program::instruction::Instruction; use solana_program::program::invoke_signed_unchecked; -use super::create_account::create_account_reliably; // TODO: Use this everywhere (but note that there is a bug with index zero on cpi) +use super::create_account::create_account_reliably; use solana_program::keccak; use anchor_lang::Discriminator; use solana_program::program_pack::Pack; @@ -114,6 +114,8 @@ impl VaaMessageBodyHeader { Self { consistency_level, vaa_time, nonce: 0, sequence, emitter_chain, emitter_address } } + /// This function creates both the message body and the payload. + /// This is all done here just because it's (supposedly?) cheaper in the solana vm. pub fn message_body(&self, fast_market_order: &FastMarketOrderState) -> Vec { let mut message_body = vec![]; message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 3c833e52c..b6ebd8a4a 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -4,15 +4,19 @@ use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::place_initial_offer::place_initial_offer_cctp_shim; +use super::execute_order::ExecuteOrderCctpShimData; +use super::execute_order::handle_execute_order_shim; impl<'ix> FallbackMatchingEngineInstruction<'ix> { pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); + pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:execute_order_cctp_shim"); } pub enum FallbackMatchingEngineInstruction<'ix> { PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), + ExecuteOrderCctpShim(&'ix ExecuteOrderCctpShimData), } pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { @@ -24,6 +28,9 @@ pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instru match instruction { FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, &data) + }, + FallbackMatchingEngineInstruction::ExecuteOrderCctpShim(data) => { + handle_execute_order_shim(accounts, &data) } } } @@ -38,6 +45,9 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { Some(Self::PlaceInitialOfferCctpShim(&PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) }, + FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { + Some(Self::ExecuteOrderCctpShim(&ExecuteOrderCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) + }, _ => None, } } @@ -62,6 +72,17 @@ impl FallbackMatchingEngineInstruction<'_> { out }, + Self::ExecuteOrderCctpShim(data) => { + let data_slice = bytemuck::bytes_of(*data); + let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + + let mut out = Vec::with_capacity(total_capacity); + + out.extend_from_slice(&FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR); + out.extend_from_slice(data_slice); + + out + } } } } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index dd6aa3dfc..14354008f 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -2,10 +2,11 @@ use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; mod utils; +use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; use utils::initialize::initialize_program; -use utils::auction::{AuctionAccounts, place_initial_offer, improve_offer}; +use utils::auction::{improve_offer, place_initial_offer, AuctionAccounts, AuctionOfferFixture}; use utils::setup::{PreTestingContext, TestingContext}; use utils::vaa::create_vaas_test_with_chain_and_address; use utils::shims::{ @@ -97,7 +98,7 @@ pub async fn test_setup_vaas() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, None, true); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; let first_test_ft = vaas_test.0.first().unwrap(); @@ -157,67 +158,6 @@ pub async fn test_post_message_shims() { } -// // TODO: Check that you cannot execute the order the old way and then place the initial offer using the shim -// /// This test should FAIL because of stack overflow issues. -// #[tokio::test] -// pub async fn test_verify_shims() { -// let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); -// pre_testing_context.add_verify_shims(); -// // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. -// let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); -// let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - -// let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); -// let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; -// // TODO: Change the posting of the signatures to be the actual single guardian signature. -// let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; -// let first_test_ft = vaas_test.0.first().unwrap(); -// // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim - -// let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); -// // Try making initial offer using the shim instruction -// let usdc_mint_address = USDC_MINT_ADDRESS; -// let auction_config_address = initialize_fixture.get_auction_config_address(); -// let router_endpoints = create_all_router_endpoints_test( -// &testing_context.test_context, -// testing_context.testing_actors.owner.pubkey(), -// initialize_fixture.get_custodian_address(), -// fixture_accounts.arbitrum_remote_token_messenger, -// fixture_accounts.ethereum_remote_token_messenger, -// usdc_mint_address, -// testing_context.testing_actors.owner.keypair(), -// PROGRAM_ID, -// ).await; -// let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; -// let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; - -// let solver = testing_context.testing_actors.solvers[0].clone(); -// let auction_accounts = AuctionAccounts::new( -// None, // Fast VAA pubkey -// solver.clone(), // Solver -// auction_config_address.clone(), // Auction config pubkey -// arb_endpoint_address, // From router endpoint pubkey -// eth_endpoint_address, // To router endpoint pubkey -// initialize_fixture.get_custodian_address(), // Custodian pubkey -// usdc_mint_address, // USDC mint pubkey -// ); - -// let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - - -// let solver = testing_context.testing_actors.solvers[0].clone(); - -// let _initial_offer_fixture = place_initial_offer_shim( -// &testing_context.test_context, -// &testing_context.testing_actors.owner.keypair(), -// &PROGRAM_ID, -// &CORE_BRIDGE_PROGRAM_ID, -// &vaa_data, -// solver, -// &auction_accounts, -// ).await.expect("Failed to place initial offer"); -// } - #[tokio::test] pub async fn test_verify_shims_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); @@ -226,12 +166,11 @@ pub async fn test_verify_shims_fallback() { let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address); + // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - // TODO: Change the posting of the signatures to be the actual single guardian signature. let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; let first_test_ft = vaas_test.0.first().unwrap(); - // Assume this vaa was not actually posted, but instead we will use it to test the new instruction using a shim let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction @@ -263,16 +202,93 @@ pub async fn test_verify_shims_fallback() { let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + // Place initial offer using the fallback program + let initial_offer_fixture = place_initial_offer_fallback( + &testing_context.test_context, + &testing_context.testing_actors.owner.keypair(), + &PROGRAM_ID, + &CORE_BRIDGE_PROGRAM_ID, + &vaa_data, + solver.clone(), + &auction_accounts, + 1__000_000, // 1 USDC (double underscore for decimal separator) + ).await.expect("Failed to place initial offer"); + + let auction_offer_fixture = AuctionOfferFixture { + auction_address: initial_offer_fixture.auction_address, + auction_custody_token_address: initial_offer_fixture.auction_custody_token_address, + offer_price: 1__000_000, + offer_token: auction_accounts.offer_token, + }; + // Attempt to improve the offer using the non-fallback method + let _improved_offer_fixture = improve_offer(&testing_context.test_context, auction_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; + // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; +} + +#[tokio::test] +pub async fn test_execute_order_fallback() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0), false); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let actors = testing_context.testing_actors; + let payer_signer = actors.owner.keypair(); + let first_test_ft = vaas_test.0.first().unwrap(); + let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); - let solver = testing_context.testing_actors.solvers[0].clone(); + // Try making initial offer using the shim instruction + let usdc_mint_address = USDC_MINT_ADDRESS; + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = create_all_router_endpoints_test( + &testing_context.test_context, + actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + fixture_accounts.arbitrum_remote_token_messenger, + fixture_accounts.ethereum_remote_token_messenger, + usdc_mint_address, + actors.owner.keypair(), + PROGRAM_ID, + ).await; + let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; + let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + let solver: utils::setup::Solver = actors.solvers[0].clone(); - let _initial_offer_fixture = place_initial_offer_fallback( + let auction_accounts = AuctionAccounts::new( + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + arb_endpoint_address, // From router endpoint pubkey + eth_endpoint_address, // To router endpoint pubkey + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + ); + + let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + + + // Place initial offer using the fallback program + let initial_offer_fixture = place_initial_offer_fallback( &testing_context.test_context, - &testing_context.testing_actors.owner.keypair(), + &payer_signer, &PROGRAM_ID, &CORE_BRIDGE_PROGRAM_ID, &vaa_data, - solver, + solver.clone(), &auction_accounts, + 1__000_000, // 1 USDC (double underscore for decimal separator) ).await.expect("Failed to place initial offer"); + + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture); + // Try executing the order using the fallback program + let _execute_order_fixture = execute_order_fallback( + &testing_context.test_context, + &payer_signer, + &PROGRAM_ID, + solver, + &execute_order_fallback_accounts, + initial_offer_fixture.fast_market_order, + ).await.expect("Failed to execute order"); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index d6fcb06ae..3b6c441ad 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -15,5 +15,5 @@ pub mod vaa; pub mod auction; pub mod setup; pub mod shims; -// pub mod shims_execute_order; +pub mod shims_execute_order; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index d22cd944b..b6a9d438b 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -21,19 +21,12 @@ use wormhole_svm_definitions::{ find_shim_message_address, }; use base64::Engine; -use matching_engine::{accounts::{CheckedCustodian, - // FastOrderPathShim, - LiveRouterEndpoint, LiveRouterPath}, state::{Auction, FastMarketOrder as FastMarketOrderState}}; -// use matching_engine::instruction::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimIx; -// use matching_engine::accounts::PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimAccounts; -use matching_engine::matching_engine::fallback_process_instruction; +use matching_engine::{state::{Auction, FastMarketOrder as FastMarketOrderState}}; use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, }; -use anchor_lang::InstructionData; -use solana_sdk::instruction::Instruction; use wormhole_svm_definitions::borsh::GuardianSignatures; #[allow(dead_code)] @@ -353,89 +346,13 @@ pub struct PlaceInitialOfferShimFixture { pub auction_custody_token_address: Pubkey, pub guardian_set_pubkey: Pubkey, pub guardian_signatures_pubkey: Pubkey, - + pub fast_market_order_address: Pubkey, + pub fast_market_order: FastMarketOrderState, } -// pub async fn place_initial_offer_shim(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { -// // The auction address? is this needed? -// let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_data.digest()], &program_id).0; -// let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; -// let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); - -// let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); -// let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); -// let signatures_signer = Rc::new(Keypair::new()); -// let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); - -// let vaa_message = matching_engine::VaaMessage::from_vec(vaa_data.message_vec()); - -// let vaa_digest = vaa_message.digest(); - -// let offer_price: u32 = 1__000_000; -// let place_initial_offer_ix_data = PlaceInitialOfferCctpShimIx { -// offer_price, -// guardian_set_bump, -// vaa_message, -// }.data(); - -// // Approve the transfer authority -// let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; -// solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; -// let checked_custodian = CheckedCustodian { -// custodian: accounts.custodian, -// }; - -// let fast_order_path_shim = FastOrderPathShim { -// guardian_set: guardian_set_pubkey, -// guardian_set_signatures: guardian_signatures_pubkey.clone().to_owned(), -// live_router_path: LiveRouterPath { -// from_endpoint: LiveRouterEndpoint { -// endpoint: accounts.from_router_endpoint, -// }, -// to_endpoint: LiveRouterEndpoint { -// endpoint: accounts.to_router_endpoint, -// }, -// }, -// }; - -// let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; -// let fast_market_order_acc = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, vaa_digest.as_ref()], program_id).0; -// let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimAccounts { -// payer: payer_signer.pubkey(), -// transfer_authority, -// custodian: checked_custodian, -// auction_config: accounts.auction_config, -// fast_order_path_shim, -// fast_market_order: fast_market_order_acc, -// auction: auction_address, -// offer_token: accounts.offer_token, -// auction_custody_token: auction_custody_token_address, -// usdc: matching_engine::accounts::Usdc { mint: accounts.usdc_mint }, -// verify_vaa_shim_program: WORMHOLE_VERIFY_VAA_SHIM_PID, -// system_program: solana_program::system_program::ID, -// token_program: anchor_spl::token::spl_token::ID, -// }; -// let place_initial_offer_ix = Instruction { -// program_id: program_id.clone().to_owned(), -// accounts: place_initial_offer_ix_accounts.to_account_metas(Some(false)), -// data: place_initial_offer_ix_data, -// }; -// let recent_blockhash = test_ctx.borrow().last_blockhash; -// let transaction = Transaction::new_signed_with_payer(&[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); - -// test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to place initial offer"); - -// Ok(PlaceInitialOfferShimFixture { -// auction_address, -// auction_custody_token_address, -// guardian_set_pubkey, -// guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), -// }) -// } - -pub async fn place_initial_offer_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, accounts: &super::auction::AuctionAccounts) -> Result { +/// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. +pub async fn place_initial_offer_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, auction_accounts: &super::auction::AuctionAccounts, offer_price: u64) -> Result { - // TODO: Make a new clean PostedVaaData struct from scratch and use that. Make sure nonce is 0. let vaa_data = super::vaa::PostedVaaData { consistency_level: vaa_data.consistency_level, vaa_time: vaa_data.vaa_time, @@ -467,7 +384,6 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc Self { + Self { + custodian: auction_accounts.custodian, + fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, + active_auction: place_initial_offer_fixture.auction_address, + active_auction_custody_token: place_initial_offer_fixture.auction_custody_token_address, + active_auction_config: auction_accounts.auction_config, + active_auction_best_offer_token: auction_accounts.offer_token, + initial_offer_token: auction_accounts.offer_token, + initial_participant: auction_accounts.solver.actor.pubkey(), + to_router_endpoint: auction_accounts.to_router_endpoint, + } + } +} + +pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, fast_market_order: FastMarketOrderState) -> Result<()> { + + // Get target chain and use as remote address + let target_chain = fast_market_order.target_chain; + + let cctp_message = Pubkey::find_program_address(&[b"cctp-msg", &execute_order_fallback_accounts.active_auction.to_bytes()], program_id).0; + let token_messenger_minter_sender_authority = Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let messenger_transmitter_config = Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let token_messenger = Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let remote_token_messenger = Pubkey::find_program_address(&[b"remote_token_messenger", &target_chain.to_string().as_bytes()], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let token_minter = Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let local_token = Pubkey::find_program_address(&[b"local_token", &USDC_MINT.to_bytes()], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let token_messenger_minter_event_authority = &Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address(&execute_order_fallback_accounts.custodian, &CORE_BRIDGE_PROGRAM_ID).0; + let post_message_message = wormhole_svm_definitions::find_shim_message_address(&execute_order_fallback_accounts.custodian, &POST_MESSAGE_SHIM_PROGRAM_ID).0; + let executor_token = solver.actor.token_account_address().unwrap(); + let execute_order_ix_accounts = ExecuteOrderShimAccounts { + signer: &payer_signer.pubkey(), + cctp_message: &cctp_message, + custodian: &execute_order_fallback_accounts.custodian, + fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, + active_auction: &execute_order_fallback_accounts.active_auction, + active_auction_custody_token: &execute_order_fallback_accounts.active_auction_custody_token, + active_auction_config: &execute_order_fallback_accounts.active_auction_config, + active_auction_best_offer_token: &execute_order_fallback_accounts.active_auction_best_offer_token, + executor_token: &executor_token, + initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, + initial_participant: &execute_order_fallback_accounts.initial_participant, + to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, + post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, + post_message_sequence: &post_message_sequence, + post_message_message: &post_message_message, + cctp_deposit_for_burn_mint: &USDC_MINT, + cctp_deposit_for_burn_token_messenger_minter_sender_authority: &token_messenger_minter_sender_authority, + cctp_deposit_for_burn_message_transmitter_config: &messenger_transmitter_config, + cctp_deposit_for_burn_token_messenger: &token_messenger, + cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, + cctp_deposit_for_burn_token_minter: &token_minter, + cctp_deposit_for_burn_local_token: &local_token, + cctp_deposit_for_burn_token_messenger_minter_event_authority: &token_messenger_minter_event_authority, + cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, + system_program: &solana_program::system_program::ID, + token_program: &spl_token::ID, + clock: &solana_program::clock::Clock::id(), + rent: &solana_program::rent::Rent::id(), + }; + + let execute_order_ix_data = ExecuteOrderCctpShimData::new( + target_chain as u32, + ); + + let execute_order_ix = ExecuteOrderCctpShim { + program_id: program_id, + accounts: execute_order_ix_accounts, + data: execute_order_ix_data, + }.instruction(); + + // Considering fast forwarding blocks here for deadline to be reached + let recent_blockhash = test_ctx.borrow().last_blockhash; + let transaction = Transaction::new_signed_with_payer(&[execute_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); + test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to execute order"); + + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 7aa4fd62d..8e6fc1c81 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -97,18 +97,6 @@ impl PostedVaaData { ]) } - pub fn message_vec(&self) -> Vec { - vec![ - self.vaa_time.to_be_bytes().as_ref(), - self.nonce.to_be_bytes().as_ref(), - self.emitter_chain.to_be_bytes().as_ref(), - &self.emitter_address, - &self.sequence.to_be_bytes(), - &[self.consistency_level], - self.payload.as_ref(), - ].concat() - } - pub fn sign_with_guardian_key(&self, guardian_secret_key: &SecpSecretKey, index: u8) -> [u8; 66] { // Sign the message hash with the guardian key let secp = secp256k1::SECP256K1; @@ -295,23 +283,41 @@ impl TestFastTransfers { } /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer - pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey) { - let sequence = self.len() as u64; - let cctp_nonce = sequence; + pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey, sequence: Option, nonce: Option) { + let sequence = sequence.unwrap_or(self.len() as u64); + let cctp_nonce = nonce.unwrap_or(sequence); let test_fast_transfer = TestFastTransfer::new(start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient); self.0.push(test_fast_transfer); } + + pub fn create_vaas_with_chain_and_address(&mut self, program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32], sequence: Option, nonce: Option, add_fast_transfer_to_test: bool) { + let source_address = ChainAddress::new_with_address(source_chain, source_address); + let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); + let refund_address = source_address.clone(); + self.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient, sequence, nonce); + if add_fast_transfer_to_test { + for test_fast_transfer in self.0.iter() { + test_fast_transfer.add_to_test(program_test); + } + } + } } -pub fn create_vaas_test_with_chain_and_address(program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32]) -> TestFastTransfers { +pub fn create_vaas_test_with_chain_and_address( + program_test: &mut ProgramTest, + mint_address: Pubkey, + start_timestamp: Option, + cctp_mint_recipient: Pubkey, + source_chain: Chain, + destination_chain: Chain, + source_address: [u8; 32], + destination_address: [u8; 32], + sequence: Option, + nonce: Option, + add_fast_transfer_to_test: bool +) -> TestFastTransfers { let mut test_fast_transfers = TestFastTransfers::new(); - let source_address = ChainAddress::new_with_address(source_chain, source_address); - let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); - let refund_address = source_address.clone(); - test_fast_transfers.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient); - for test_fast_transfer in test_fast_transfers.0.iter() { - test_fast_transfer.add_to_test(program_test); - } + test_fast_transfers.create_vaas_with_chain_and_address(program_test, mint_address, start_timestamp, cctp_mint_recipient, source_chain, destination_chain, source_address, destination_address, sequence, nonce, add_fast_transfer_to_test); test_fast_transfers } pub trait ToBytes { From 243e4978c0f44f624c6ddb3c1aa064eb3311ffed Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 4 Mar 2025 23:06:20 +0000 Subject: [PATCH 014/112] [working] execute order shim fallback works test works, but test needs to be more involved --- .../src/fallback/processor/burn_and_post.rs | 6 +- .../src/fallback/processor/execute_order.rs | 191 +++++++++--------- .../fallback/processor/place_initial_offer.rs | 7 +- .../fallback/processor/process_instruction.rs | 16 +- .../tests/initialize_integration_tests.rs | 20 +- .../matching-engine/tests/utils/auction.rs | 4 +- .../matching-engine/tests/utils/setup.rs | 57 +++++- .../matching-engine/tests/utils/shims.rs | 4 +- .../tests/utils/shims_execute_order.rs | 90 +++++---- .../matching-engine/tests/utils/vaa.rs | 84 ++++++-- 10 files changed, 297 insertions(+), 182 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index f74b24256..47a42707f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -4,6 +4,8 @@ use wormhole_svm_shim::post_message; use wormhole_svm_definitions::solana::{CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID}; use anchor_lang::prelude::*; use wormhole_svm_definitions::{solana::Finality, find_emitter_sequence_address, find_shim_message_address}; +use crate::state::Custodian; + // This is a helper struct to make it easier to pass in the accounts for the post_message instruction. pub struct PostMessageAccounts { @@ -69,8 +71,8 @@ pub fn burn_and_post<'info>( .unwrap(), } .instruction(); - - invoke_signed_unchecked(&post_message_ix, account_infos, &[])?; + + invoke_signed_unchecked(&post_message_ix, account_infos, &[Custodian::SIGNER_SEEDS])?; deposit_for_burn_with_caller( cctp_ctx, diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 6ac49015a..1e41b62ea 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use bytemuck::{Pod, Zeroable}; use common::messages::Fill; +use solana_program::program::invoke_signed_unchecked; use crate::utils; use solana_program::instruction::Instruction; use crate::state::{Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; @@ -14,57 +14,42 @@ use super::errors::FallbackError; use crate::error::MatchingEngineError; -#[derive(Debug, Copy, Clone, Pod, Zeroable)] -#[repr(C)] -pub struct ExecuteOrderCctpShimData { - pub destination_cctp_domain: u32, - _padding: [u8; 4], -} - -impl ExecuteOrderCctpShimData { - pub fn new(destination_cctp_domain: u32) -> Self { - Self { - destination_cctp_domain, - _padding: [0; 4], - } - } - - pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() - } -} #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExecuteOrderShimAccounts<'ix> { pub signer: &'ix Pubkey, // 0 - pub cctp_message: &'ix Pubkey, // 2 - pub custodian: &'ix Pubkey, // 3 - pub fast_market_order: &'ix Pubkey, // 4 - pub active_auction: &'ix Pubkey, // 5 - pub active_auction_custody_token: &'ix Pubkey, // 6 - pub active_auction_config: &'ix Pubkey, // 7 - pub active_auction_best_offer_token: &'ix Pubkey, // 8 - pub executor_token: &'ix Pubkey, // 9 - pub initial_offer_token: &'ix Pubkey, // 10 - pub initial_participant: &'ix Pubkey, // 11 - pub to_router_endpoint: &'ix Pubkey, // 12 + pub cctp_message: &'ix Pubkey, // 1 + pub custodian: &'ix Pubkey, // 2 + pub fast_market_order: &'ix Pubkey, // 3 + pub active_auction: &'ix Pubkey, // 4 + pub active_auction_custody_token: &'ix Pubkey, // 5 + pub active_auction_config: &'ix Pubkey, // 6 + pub active_auction_best_offer_token: &'ix Pubkey, // 7 + pub executor_token: &'ix Pubkey, // 8 + pub initial_offer_token: &'ix Pubkey, // 9 + pub initial_participant: &'ix Pubkey, // 10 + pub to_router_endpoint: &'ix Pubkey, // 11 // Add shim post message accounts. TODO: Ask about if these are needed at all, or if they can just be imported/derived - pub post_message_shim_program: &'ix Pubkey, // 13 - pub post_message_sequence: &'ix Pubkey, // 14 - pub post_message_message: &'ix Pubkey, // 15 - pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 16 - pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 17 - pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 18 - pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 19 - pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 20 - pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 21 - pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 22 - pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 23 - pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 24 - pub cctp_deposit_for_burn_message_transmitter_program: &'ix Pubkey, // 25 - pub system_program: &'ix Pubkey, // 26 - pub token_program: &'ix Pubkey, // 27 - pub clock: &'ix Pubkey, // 28 - pub rent: &'ix Pubkey, // 29 + pub post_message_shim_program: &'ix Pubkey, // 12 + pub post_message_sequence: &'ix Pubkey, // 13 + pub post_message_message: &'ix Pubkey, // 14 + pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 15 + pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 16 + pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 17 + pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 18 + pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 19 + pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 20 + pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 21 + pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 22 + pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 23 + pub cctp_deposit_for_burn_message_transmitter_program: &'ix Pubkey, // 24 + // Core bridge program accounts + pub core_bridge_program: &'ix Pubkey, // 25 + pub core_bridge_config: &'ix Pubkey, // 26 + pub core_bridge_fee_collector: &'ix Pubkey, // 27 + pub post_message_shim_event_authority: &'ix Pubkey, // 28 + pub system_program: &'ix Pubkey, // 29 + pub token_program: &'ix Pubkey, // 30 + pub clock: &'ix Pubkey, // 31 } impl<'ix> ExecuteOrderShimAccounts<'ix> { @@ -95,11 +80,13 @@ impl<'ix> ExecuteOrderShimAccounts<'ix> { AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_event_authority, false), AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_program, false), AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_program, false), - AccountMeta::new(*self.post_message_shim_program, false), + AccountMeta::new(*self.core_bridge_program, false), + AccountMeta::new(*self.core_bridge_config, false), + AccountMeta::new(*self.core_bridge_fee_collector, false), + AccountMeta::new(*self.post_message_shim_event_authority, false), AccountMeta::new(*self.system_program, false), AccountMeta::new(*self.token_program, false), AccountMeta::new(*self.clock, false), - AccountMeta::new(*self.rent, false), ] } } @@ -107,7 +94,6 @@ impl<'ix> ExecuteOrderShimAccounts<'ix> { pub struct ExecuteOrderCctpShim<'ix> { pub program_id: &'ix Pubkey, pub accounts: ExecuteOrderShimAccounts<'ix>, - pub data: ExecuteOrderCctpShimData, } impl ExecuteOrderCctpShim<'_> { @@ -115,49 +101,48 @@ impl ExecuteOrderCctpShim<'_> { Instruction { program_id: *self.program_id, accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::ExecuteOrderCctpShim(&self.data).to_vec(), + data: FallbackMatchingEngineInstruction::ExecuteOrderCctpShim.to_vec(), } } } -pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCctpShimData) -> Result<()> { +pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let program_id = &crate::ID; // Get the accounts let signer_account = &accounts[0]; - let cctp_message_account = &accounts[2]; - let custodian_account = &accounts[3]; - let fast_market_order_account = &accounts[4]; - let active_auction_account = &accounts[5]; - let active_auction_custody_token_account = &accounts[6]; - let active_auction_config_account = &accounts[7]; - let active_auction_best_offer_token_account = &accounts[8]; - let executor_token_account = &accounts[9]; - let initial_offer_token_account = &accounts[10]; - let initial_participant_account = &accounts[11]; - let to_router_endpoint_account = &accounts[12]; + let cctp_message_account = &accounts[1]; + let custodian_account = &accounts[2]; + let fast_market_order_account = &accounts[3]; + let active_auction_account = &accounts[4]; + let active_auction_custody_token_account = &accounts[5]; + let active_auction_config_account = &accounts[6]; + let active_auction_best_offer_token_account = &accounts[7]; + let executor_token_account = &accounts[8]; + let initial_offer_token_account = &accounts[9]; + let initial_participant_account = &accounts[10]; + let to_router_endpoint_account = &accounts[11]; // TODO: These are not used, so can I just ignore them? - let post_message_shim_program_account = &accounts[13]; - let post_message_sequence_account = &accounts[14]; - let post_message_message_account = &accounts[15]; - let cctp_deposit_for_burn_mint_account = &accounts[16]; - let cctp_deposit_for_burn_token_messenger_minter_sender_authority_account = &accounts[17]; - let cctp_deposit_for_burn_message_transmitter_config_account = &accounts[18]; - let cctp_deposit_for_burn_token_messenger_account = &accounts[19]; - let cctp_deposit_for_burn_remote_token_messenger_account = &accounts[20]; - let cctp_deposit_for_burn_token_minter_account = &accounts[21]; - let cctp_deposit_for_burn_local_token_account = &accounts[22]; - let cctp_deposit_for_burn_token_messenger_minter_event_authority_account = &accounts[23]; - let cctp_deposit_for_burn_token_messenger_minter_program_account = &accounts[24]; - let cctp_deposit_for_burn_message_transmitter_program_account = &accounts[25]; - let system_program_account = &accounts[26]; - let token_program_account = &accounts[27]; - let _clock_account = &accounts[28]; - let _rent_account = &accounts[29]; - - let ExecuteOrderCctpShimData { - destination_cctp_domain, - _padding, - } = data; + // TODO: Check that this is the correct program id for the post message shim program + let _post_message_shim_program_account = &accounts[12]; + let _post_message_sequence_account = &accounts[13]; + let _post_message_message_account = &accounts[14]; + let cctp_deposit_for_burn_mint_account = &accounts[15]; + let cctp_deposit_for_burn_token_messenger_minter_sender_authority_account = &accounts[16]; + let cctp_deposit_for_burn_message_transmitter_config_account = &accounts[17]; + let cctp_deposit_for_burn_token_messenger_account = &accounts[18]; + let cctp_deposit_for_burn_remote_token_messenger_account = &accounts[19]; + let cctp_deposit_for_burn_token_minter_account = &accounts[20]; + let cctp_deposit_for_burn_local_token_account = &accounts[21]; + let cctp_deposit_for_burn_token_messenger_minter_event_authority_account = &accounts[22]; + let cctp_deposit_for_burn_token_messenger_minter_program_account = &accounts[23]; + let cctp_deposit_for_burn_message_transmitter_program_account = &accounts[24]; + let _core_bridge_program_account = &accounts[25]; + let _core_bridge_config_account = &accounts[26]; + let _core_bridge_fee_collector_account = &accounts[27]; + let _post_message_shim_event_authority_account = &accounts[28]; + let system_program_account = &accounts[29]; + let token_program_account = &accounts[30]; + // Do checks // ------------------------------------------------------------------------------------------------ @@ -177,6 +162,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc common::CCTP_MESSAGE_SEED_PREFIX, active_auction_key.as_ref(), ]; + let (cctp_message_pda, cctp_message_bump) = Pubkey::find_program_address(&cctp_message_seeds, program_id); if cctp_message_pda != cctp_message_account.key() { msg!("Cctp message seeds are invalid"); @@ -204,6 +190,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc FastMarketOrderState::SEED_PREFIX, active_auction_key.as_ref(), ]; + let (fast_market_order_pda, _fast_market_order_bump) = Pubkey::find_program_address(&fast_market_order_seeds, program_id); if fast_market_order_pda != fast_market_order_account.key() { msg!("Fast market order seeds are invalid"); @@ -287,11 +274,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc }; // TODO: Done with auction checks, now on to executor token checks - let executor_token = anchor_spl::token::TokenAccount::try_deserialize(&mut &executor_token_account.data.borrow()[..])?; - if executor_token.mint != active_auction.info.as_ref().unwrap().best_offer_token { - msg!("Executor token mint is invalid"); + if executor_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { + msg!("Executor token is not equal to best offer token"); return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((executor_token.mint, active_auction.info.as_ref().unwrap().best_offer_token))); + .map_err(|e: Error| e.with_pubkeys((executor_token_account.key(), active_auction.info.as_ref().unwrap().best_offer_token))); }; // Check initial offer token address @@ -315,7 +301,12 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc .map_err(|e: Error| e.with_account_name("to_router_endpoint")); }; - // TODO: Ask about seeds for wormhole publish message accounts + let destination_cctp_domain = match to_router_endpoint.protocol { + MessageProtocol::Cctp { domain } => domain, + _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()) + .map_err(|e: Error| e.with_account_name("to_router_endpoint")), + }; + // Check cctp deposit for burn token messenger minter program address if cctp_deposit_for_burn_token_messenger_minter_program_account.key() != common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id() { @@ -334,7 +325,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc // End of checks // ------------------------------------------------------------------------------------------------ - let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; + // Get the fast market order data, without the discriminator + let fast_market_order_data = &fast_market_order_account.data.borrow()[8..]; // Deserialise fast market order. Unwrap is safe because the account is owned by the matching engine program. let fast_market_order = bytemuck::try_from_bytes::( &fast_market_order_data[..] @@ -367,7 +359,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc ); let init_auction_fee = fast_market_order.init_auction_fee; - + let user_amount = active_auction_info .amount_in .saturating_sub(active_auction_info.offer_price) @@ -420,7 +412,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc } // Return the security deposit and the fee to the highest bidder. - // if active_auction_best_offer_token_account.key() == executor_token_account.key() { // If the best offer token is equal to the executor token, just send whatever remains in // the custody token account. @@ -470,7 +461,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc // Set the authority of the custody token account to the custodian. He will take over from // here. - spl_token::instruction::set_authority( + let set_authority_ix = spl_token::instruction::set_authority( &spl_token::ID, &active_auction_custody_token_account.key(), Some(&custodian_account.key()), @@ -479,6 +470,13 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc &[], ).unwrap(); + let auction_signer_seeds = &[ + Auction::SEED_PREFIX, + active_auction.vaa_hash.as_ref(), + &[active_auction.bump], + ]; + invoke_signed_unchecked(&set_authority_ix, accounts, &[auction_signer_seeds])?; + // Set the active auction status active_auction.status = AuctionStatus::Completed { slot: current_slot, @@ -493,7 +491,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc }; let post_message_accounts = PostMessageAccounts::new(custodian_account.key(), signer_account.key()); - burn_and_post( CpiContext::new_with_signer( cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), @@ -527,7 +524,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo], data: &ExecuteOrderCc common::wormhole_cctp_solana::cpi::BurnAndPublishArgs { burn_source: None, destination_caller: to_router_endpoint.address, - destination_cctp_domain: *destination_cctp_domain, + destination_cctp_domain: destination_cctp_domain, amount: user_amount, mint_recipient: to_router_endpoint.mint_recipient, wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 0adb54505..449c0fd83 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -49,7 +49,7 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub guardian_set_signatures: &'ix Pubkey, pub from_endpoint: &'ix Pubkey, pub to_endpoint: &'ix Pubkey, - pub fast_market_order: &'ix Pubkey, // Needs initalising + pub fast_market_order: &'ix Pubkey, // Needs initalising. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] pub auction: &'ix Pubkey, // Needs initalising pub offer_token: &'ix Pubkey, pub auction_custody_token: &'ix Pubkey, @@ -62,6 +62,7 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { impl<'ix> PlaceInitialOfferCctpShimAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ + // TODO: Change some to read only using the new_readonly AccountMeta::new(*self.signer, true), AccountMeta::new(*self.transfer_authority, false), AccountMeta::new(*self.custodian, false), @@ -367,7 +368,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrderState::SEED_PREFIX, - vaa_message_digest.as_ref(), + auction_key.as_ref(), ], &program_id, ); @@ -378,7 +379,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti } let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, - vaa_message_digest.as_ref(), + auction_key.as_ref(), &[fast_market_order_bump], ]; let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index b6ebd8a4a..090dad76b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -4,7 +4,6 @@ use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::place_initial_offer::place_initial_offer_cctp_shim; -use super::execute_order::ExecuteOrderCctpShimData; use super::execute_order::handle_execute_order_shim; @@ -16,7 +15,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { pub enum FallbackMatchingEngineInstruction<'ix> { PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), - ExecuteOrderCctpShim(&'ix ExecuteOrderCctpShimData), + ExecuteOrderCctpShim, } pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { @@ -29,8 +28,8 @@ pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instru FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, &data) }, - FallbackMatchingEngineInstruction::ExecuteOrderCctpShim(data) => { - handle_execute_order_shim(accounts, &data) + FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { + handle_execute_order_shim(accounts) } } } @@ -46,7 +45,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { Some(Self::PlaceInitialOfferCctpShim(&PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) }, FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { - Some(Self::ExecuteOrderCctpShim(&ExecuteOrderCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) + Some(Self::ExecuteOrderCctpShim) }, _ => None, } @@ -72,14 +71,13 @@ impl FallbackMatchingEngineInstruction<'_> { out }, - Self::ExecuteOrderCctpShim(data) => { - let data_slice = bytemuck::bytes_of(*data); - let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + Self::ExecuteOrderCctpShim => { + let total_capacity = 8; // 8 for the selector (no data) let mut out = Vec::with_capacity(total_capacity); out.extend_from_slice(&FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR); - out.extend_from_slice(data_slice); + out } diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 14354008f..58f60af10 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -2,6 +2,7 @@ use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; mod utils; +use solana_sdk::signer::Signer; use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; @@ -92,6 +93,7 @@ pub async fn test_local_token_router_endpoint_creation() { } // Test setting up vaas +// Vaa is from arbitrum to ethereum // - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs #[tokio::test] pub async fn test_setup_vaas() { @@ -159,6 +161,8 @@ pub async fn test_post_message_shims() { #[tokio::test] +// Testing a initial offer from arbitrum to ethereum +// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction pub async fn test_verify_shims_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); pre_testing_context.add_verify_shims(); @@ -225,10 +229,14 @@ pub async fn test_verify_shims_fallback() { // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } + #[tokio::test] +// Testing an execute order from arbitrum to ethereum +// TODO: Flesh out this test to see if the message was posted correctly pub async fn test_execute_order_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); pre_testing_context.add_verify_shims(); + pre_testing_context.add_post_message_shims(); let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0), false); @@ -267,8 +275,9 @@ pub async fn test_execute_order_fallback() { ); let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - + println!("Solver balance before placing initial offer: {:?}", solver.get_balance(&testing_context.test_context).await); + // Place initial offer using the fallback program let initial_offer_fixture = place_initial_offer_fallback( &testing_context.test_context, @@ -280,15 +289,18 @@ pub async fn test_execute_order_fallback() { &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) ).await.expect("Failed to place initial offer"); + + println!("Solver balance after placing initial offer: {:?}", solver.get_balance(&testing_context.test_context).await); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture); + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture, &payer_signer.pubkey(), &fixture_accounts); // Try executing the order using the fallback program let _execute_order_fixture = execute_order_fallback( &testing_context.test_context, &payer_signer, &PROGRAM_ID, - solver, + solver.clone(), &execute_order_fallback_accounts, - initial_offer_fixture.fast_market_order, ).await.expect("Failed to execute order"); + + println!("Solver balance after executing order: {:?}", solver.get_balance(&testing_context.test_context).await); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 1bd123b1d..dc71e80f4 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -63,8 +63,8 @@ impl AuctionOfferFixture { best_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata initial_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata start_slot: 1, - amount_in: 1000, - security_deposit: 1_004__200_005, + amount_in: 69000000, + security_deposit: 10545000, offer_price: 1__000_000, redeemer_message_len: 0, destination_asset_info: None, diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 99cabc287..e4b753680 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -1,10 +1,11 @@ +use anchor_lang::AccountDeserialize; use solana_program_test::{ProgramTest, ProgramTestContext}; use solana_sdk::{ - pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction }; use std::rc::Rc; use std::cell::RefCell; -use anchor_spl::token::spl_token::{self, instruction::approve}; +use anchor_spl::token::{spl_token::{self, instruction::approve}, TokenAccount}; use super::{airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}}; use super::mint::MintFixture; use super::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router, initialise_post_message_shims, initialise_verify_shims}; @@ -127,6 +128,10 @@ impl Solver { ); test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); } + + pub async fn get_balance(&self, test_context: &Rc>) -> u64 { + self.actor.get_balance(test_context).await + } } #[derive(Clone)] @@ -155,6 +160,17 @@ impl TestingActor { pub fn token_account_address(&self) -> Option { self.token_account.as_ref().map(|t| t.address) } + + pub async fn get_balance(&self, test_context: &Rc>) -> u64 { + if let Some(token_account) = self.token_account_address() { + let account = test_context.borrow_mut().banks_client.get_account(token_account).await.unwrap().unwrap(); + let token_account = TokenAccount::try_deserialize(&mut &account.data[..]).unwrap(); + token_account.amount + } + else { + 0 + } + } } /// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) @@ -226,4 +242,41 @@ impl TestingActors { self.solvers.push(Solver::new(keypair.clone(), Some(usdc_ata))); } } +} + +pub async fn fast_forward_slots(test_context: &Rc>, num_slots: u64) { + // Get the current slot + let mut current_slot = test_context + .borrow_mut() + .banks_client + .get_root_slot() + .await + .unwrap(); + + let target_slot = current_slot + num_slots; + while current_slot < target_slot { + // Warp to the next slot - note we need to borrow_mut() here + test_context + .borrow_mut() + .warp_to_slot(current_slot + 1) + .expect("Failed to warp to slot"); + current_slot += 1; + } + + // Optionally, process a transaction to ensure the new slot is recognized + let recent_blockhash = test_context.borrow().last_blockhash; + let payer = test_context.borrow().payer.pubkey(); + let tx = Transaction::new_signed_with_payer( + &[], + Some(&payer), + &[&test_context.borrow().payer], + recent_blockhash, + ); + + test_context + .borrow_mut() + .banks_client + .process_transaction(tx) + .await + .expect("Failed to process transaction after warping"); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index b6a9d438b..3171bcc47 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -21,7 +21,7 @@ use wormhole_svm_definitions::{ find_shim_message_address, }; use base64::Engine; -use matching_engine::{state::{Auction, FastMarketOrder as FastMarketOrderState}}; +use matching_engine::state::{Auction, FastMarketOrder as FastMarketOrderState}; use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, @@ -383,7 +383,7 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc Self { + pub fn new(auction_accounts: &super::auction::AuctionAccounts, place_initial_offer_fixture: &super::shims::PlaceInitialOfferShimFixture, signer: &Pubkey, fixture_accounts: &super::account_fixtures::FixtureAccounts) -> Self { Self { custodian: auction_accounts.custodian, fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, @@ -39,72 +39,74 @@ impl ExecuteOrderFallbackAccounts { active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, - initial_participant: auction_accounts.solver.actor.pubkey(), + initial_participant: signer.clone(), to_router_endpoint: auction_accounts.to_router_endpoint, + remote_token_messenger: fixture_accounts.ethereum_remote_token_messenger, + token_messenger: fixture_accounts.token_messenger, } } } -pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, fast_market_order: FastMarketOrderState) -> Result<()> { +pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts) -> Result<()> { // Get target chain and use as remote address - let target_chain = fast_market_order.target_chain; - - let cctp_message = Pubkey::find_program_address(&[b"cctp-msg", &execute_order_fallback_accounts.active_auction.to_bytes()], program_id).0; + let cctp_message = Pubkey::find_program_address(&[common::CCTP_MESSAGE_SEED_PREFIX, &execute_order_fallback_accounts.active_auction.to_bytes()], program_id).0; let token_messenger_minter_sender_authority = Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let messenger_transmitter_config = Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; let token_messenger = Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let remote_token_messenger = Pubkey::find_program_address(&[b"remote_token_messenger", &target_chain.to_string().as_bytes()], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let remote_token_messenger = execute_order_fallback_accounts.remote_token_messenger; let token_minter = Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let local_token = Pubkey::find_program_address(&[b"local_token", &USDC_MINT.to_bytes()], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let token_messenger_minter_event_authority = &Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address(&execute_order_fallback_accounts.custodian, &CORE_BRIDGE_PROGRAM_ID).0; let post_message_message = wormhole_svm_definitions::find_shim_message_address(&execute_order_fallback_accounts.custodian, &POST_MESSAGE_SHIM_PROGRAM_ID).0; let executor_token = solver.actor.token_account_address().unwrap(); + let execute_order_ix_accounts = ExecuteOrderShimAccounts { - signer: &payer_signer.pubkey(), - cctp_message: &cctp_message, - custodian: &execute_order_fallback_accounts.custodian, - fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, - active_auction: &execute_order_fallback_accounts.active_auction, - active_auction_custody_token: &execute_order_fallback_accounts.active_auction_custody_token, - active_auction_config: &execute_order_fallback_accounts.active_auction_config, - active_auction_best_offer_token: &execute_order_fallback_accounts.active_auction_best_offer_token, - executor_token: &executor_token, - initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, - initial_participant: &execute_order_fallback_accounts.initial_participant, - to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, - post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, - post_message_sequence: &post_message_sequence, - post_message_message: &post_message_message, - cctp_deposit_for_burn_mint: &USDC_MINT, - cctp_deposit_for_burn_token_messenger_minter_sender_authority: &token_messenger_minter_sender_authority, - cctp_deposit_for_burn_message_transmitter_config: &messenger_transmitter_config, - cctp_deposit_for_burn_token_messenger: &token_messenger, - cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, - cctp_deposit_for_burn_token_minter: &token_minter, - cctp_deposit_for_burn_local_token: &local_token, - cctp_deposit_for_burn_token_messenger_minter_event_authority: &token_messenger_minter_event_authority, - cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, - cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, - system_program: &solana_program::system_program::ID, - token_program: &spl_token::ID, - clock: &solana_program::clock::Clock::id(), - rent: &solana_program::rent::Rent::id(), + signer: &payer_signer.pubkey(), // 0 + cctp_message: &cctp_message, // 1 + custodian: &execute_order_fallback_accounts.custodian, // 2 + fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, // 3 + active_auction: &execute_order_fallback_accounts.active_auction, // 4 + active_auction_custody_token: &execute_order_fallback_accounts.active_auction_custody_token, // 5 + active_auction_config: &execute_order_fallback_accounts.active_auction_config, // 6 + active_auction_best_offer_token: &execute_order_fallback_accounts.active_auction_best_offer_token, // 7 + executor_token: &executor_token, // 8 + initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, // 9 + initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 + to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 + post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 + post_message_sequence: &post_message_sequence, // 13 + post_message_message: &post_message_message, // 14 + cctp_deposit_for_burn_mint: &USDC_MINT, // 15 + cctp_deposit_for_burn_token_messenger_minter_sender_authority: &token_messenger_minter_sender_authority, // 16 + cctp_deposit_for_burn_message_transmitter_config: &messenger_transmitter_config, // 17 + cctp_deposit_for_burn_token_messenger: &token_messenger, // 18 + cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, // 19 + cctp_deposit_for_burn_token_minter: &token_minter, // 20 + cctp_deposit_for_burn_local_token: &local_token, // 21 + cctp_deposit_for_burn_token_messenger_minter_event_authority: &token_messenger_minter_event_authority, // 22 + cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 + cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 + core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 + core_bridge_config: &CORE_BRIDGE_CONFIG, // 26 + core_bridge_fee_collector: &CORE_BRIDGE_FEE_COLLECTOR, // 27 + post_message_shim_event_authority: &POST_MESSAGE_SHIM_EVENT_AUTHORITY, // 28 + system_program: &solana_program::system_program::ID, // 29 + token_program: &spl_token::ID, // 30 + clock: &solana_program::clock::Clock::id(), // 31 }; - let execute_order_ix_data = ExecuteOrderCctpShimData::new( - target_chain as u32, - ); let execute_order_ix = ExecuteOrderCctpShim { program_id: program_id, accounts: execute_order_ix_accounts, - data: execute_order_ix_data, }.instruction(); // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; + super::setup::fast_forward_slots(test_ctx, 20).await; + println!("Fast forwarded 20 slots"); let transaction = Transaction::new_signed_with_payer(&[execute_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to execute order"); diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 8e6fc1c81..ec9e12926 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -174,6 +174,48 @@ pub enum TestVaaKind { FastTransfer, } +pub struct CreateDepositAndFastTransferParams { + pub deposit_params: CreateDepositParams, + pub fast_transfer_params: CreateFastTransferParams, +} + +impl Default for CreateDepositAndFastTransferParams { + fn default() -> Self { + Self { deposit_params: CreateDepositParams::default(), fast_transfer_params: CreateFastTransferParams::default() } + } +} + +impl CreateDepositAndFastTransferParams { + pub fn verify(&self) { + assert!(self.fast_transfer_params.max_fee > self.deposit_params.base_fee + self.fast_transfer_params.init_auction_fee, "Max fee must be greater than the sum of the base fee and the init auction fee"); + assert!(self.fast_transfer_params.amount_in > self.fast_transfer_params.max_fee , "Amount in must be greater than max fee"); + } +} + +pub struct CreateDepositParams { + pub amount: i32, + pub base_fee: u64, +} + +impl Default for CreateDepositParams { + fn default() -> Self { + Self { amount: 69000000, base_fee: 0 } + } +} + +pub struct CreateFastTransferParams { + pub amount_in: u64, + pub min_amount_out: u64, + pub max_fee: u64, + pub init_auction_fee: u64, +} + +impl Default for CreateFastTransferParams { + fn default() -> Self { + Self { amount_in: 69000000, min_amount_out: 69000000, max_fee: 6000000, init_auction_fee: 10 } + } +} + pub struct TestFastTransfer { pub token_mint: Pubkey, pub source_address: ChainAddress, @@ -186,9 +228,12 @@ pub struct TestFastTransfer { } impl TestFastTransfer { - pub fn new(start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> Self { - let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient); - let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence); + pub fn new(start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams) -> Self { + create_deposit_and_fast_transfer_params.verify(); + let deposit_params = create_deposit_and_fast_transfer_params.deposit_params; + let create_fast_transfer_params = create_deposit_and_fast_transfer_params.fast_transfer_params; + let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient, deposit_params.amount, deposit_params.base_fee); + let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence, create_fast_transfer_params.amount_in, create_fast_transfer_params.min_amount_out, create_fast_transfer_params.max_fee, create_fast_transfer_params.init_auction_fee); Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data } } } @@ -216,15 +261,15 @@ impl TestFastTransfer { } } -pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey) -> (Pubkey, PostedVaaData) { +pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, amount: i32, base_fee: u64) -> (Pubkey, PostedVaaData) { let slow_order_response = SlowOrderResponse { - base_fee: 0, + base_fee, }; // Implements TypePrefixedPayload let deposit = Deposit { token_address: token_mint.to_bytes(), - amount: ruint::aliases::U256::from(100), + amount: ruint::aliases::U256::from(amount), source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain as usize].1, destination_cctp_domain: CHAIN_TO_DOMAIN[destination_address.chain as usize].1, cctp_nonce, @@ -233,6 +278,8 @@ pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, payload: WriteableBytes::new(slow_order_response.to_vec()), }; + // TODO: Checks on deposit + // Sequece == nonce in this case, since only vaas we are submitting are fast transfers let posted_vaa_data = PostedVaaData::new(source_address.chain, deposit.to_vec(), source_address.address, sequence, cctp_nonce as u32); let vaa_hash = posted_vaa_data.message_hash(); @@ -241,23 +288,25 @@ pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, (vaa_address, posted_vaa_data) } -pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64) -> (Pubkey, PostedVaaData) { +pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, amount_in: u64, min_amount_out: u64, max_fee: u64, init_auction_fee: u64) -> (Pubkey, PostedVaaData) { // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); // Implements TypePrefixedPayload let fast_market_order = FastMarketOrder { - amount_in: 1000, - min_amount_out: 1000, + amount_in, + min_amount_out, target_chain: destination_address.chain.to_chain_id(), redeemer: destination_address.address.to_bytes(), sender: source_address.address.to_bytes(), refund_address: refund_address.address.to_bytes(), // Not used so can be all zeros - max_fee: 1000000000, // USDC max fee - init_auction_fee: 10, // USDC init auction fee (the first person to verify a vaa and start an auction will get this fee) so at least rent + max_fee, // USDC max fee + init_auction_fee, // USDC init auction fee (the first person to verify a vaa and start an auction will get this fee) so at least rent deadline, // If dealine is 0 then there is no deadline redeemer_message: WriteableBytes::new(vec![]), }; + // TODO: Checks on fast transfer + let posted_vaa_data = PostedVaaData::new(source_address.chain, fast_market_order.to_vec(), source_address.address, sequence, cctp_nonce as u32); let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); @@ -283,18 +332,18 @@ impl TestFastTransfers { } /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer - pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey, sequence: Option, nonce: Option) { + pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey, sequence: Option, nonce: Option, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams) { let sequence = sequence.unwrap_or(self.len() as u64); let cctp_nonce = nonce.unwrap_or(sequence); - let test_fast_transfer = TestFastTransfer::new(start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient); + let test_fast_transfer = TestFastTransfer::new(start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient, create_deposit_and_fast_transfer_params); self.0.push(test_fast_transfer); } - pub fn create_vaas_with_chain_and_address(&mut self, program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32], sequence: Option, nonce: Option, add_fast_transfer_to_test: bool) { + pub fn create_vaas_with_chain_and_address(&mut self, program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32], sequence: Option, nonce: Option, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, add_fast_transfer_to_test: bool) { let source_address = ChainAddress::new_with_address(source_chain, source_address); let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); let refund_address = source_address.clone(); - self.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient, sequence, nonce); + self.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient, sequence, nonce, create_deposit_and_fast_transfer_params); if add_fast_transfer_to_test { for test_fast_transfer in self.0.iter() { test_fast_transfer.add_to_test(program_test); @@ -314,10 +363,11 @@ pub fn create_vaas_test_with_chain_and_address( destination_address: [u8; 32], sequence: Option, nonce: Option, - add_fast_transfer_to_test: bool + add_fast_transfer_to_test: bool, ) -> TestFastTransfers { let mut test_fast_transfers = TestFastTransfers::new(); - test_fast_transfers.create_vaas_with_chain_and_address(program_test, mint_address, start_timestamp, cctp_mint_recipient, source_chain, destination_chain, source_address, destination_address, sequence, nonce, add_fast_transfer_to_test); + let create_deposit_and_fast_transfer_params = CreateDepositAndFastTransferParams::default(); + test_fast_transfers.create_vaas_with_chain_and_address(program_test, mint_address, start_timestamp, cctp_mint_recipient, source_chain, destination_chain, source_address, destination_address, sequence, nonce, create_deposit_and_fast_transfer_params, add_fast_transfer_to_test); test_fast_transfers } pub trait ToBytes { From 43c85098c75fd687037856d876cff8ae9b697a16 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 11 Mar 2025 14:58:28 +0000 Subject: [PATCH 015/112] [broken] dumb rpcerror(deadlineexceeded) error --- .../src/fallback/processor/burn_and_post.rs | 2 + .../processor/close_fast_market_order.rs | 63 +++ .../src/fallback/processor/errors.rs | 3 + .../src/fallback/processor/execute_order.rs | 112 +++-- .../processor/initialise_fast_market_order.rs | 160 ++++++++ .../src/fallback/processor/mod.rs | 5 +- .../fallback/processor/place_initial_offer.rs | 170 ++------ .../processor/prepare_order_response.rs | 386 ++++++++++++++++++ .../fallback/processor/process_instruction.rs | 54 +++ solana/programs/matching-engine/src/lib.rs | 1 + .../src/state/fast_market_order.rs | 11 + .../fixtures/wormhole_verify_vaa_shim.so | Bin 97208 -> 97624 bytes .../tests/initialize_integration_tests.rs | 43 +- .../matching-engine/tests/utils/setup.rs | 6 +- .../matching-engine/tests/utils/shims.rs | 179 +++++--- .../tests/utils/shims_execute_order.rs | 18 +- 16 files changed, 978 insertions(+), 235 deletions(-) create mode 100644 solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index 47a42707f..f8516782c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -49,6 +49,7 @@ pub fn burn_and_post<'info>( payload, } = burn_and_publish_args; + // Post message to the shim program let post_message_ix = post_message::PostMessage { program_id: &POST_MESSAGE_SHIM_PROGRAM_ID, accounts: post_message::PostMessageAccounts { @@ -74,6 +75,7 @@ pub fn burn_and_post<'info>( invoke_signed_unchecked(&post_message_ix, account_infos, &[Custodian::SIGNER_SEEDS])?; + // Deposit for burn deposit_for_burn_with_caller( cctp_ctx, DepositForBurnWithCallerParams { diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs new file mode 100644 index 000000000..3e3e87ec9 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -0,0 +1,63 @@ +use anchor_lang::prelude::*; +use solana_program::program_error::ProgramError; +use solana_program::instruction::Instruction; +use crate::state::FastMarketOrder; + +pub struct CloseFastMarketOrderAccounts<'ix> { + pub fast_market_order: &'ix Pubkey, + pub refund_recipient: &'ix Pubkey, +} + +impl<'ix> CloseFastMarketOrderAccounts<'ix> { + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new_readonly(*self.fast_market_order, false), + AccountMeta::new_readonly(*self.refund_recipient, false), + ] + } +} + +pub struct CloseFastMarketOrder<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: CloseFastMarketOrderAccounts<'ix>, +} + +impl CloseFastMarketOrder<'_> { + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: vec![], + } + } +} + +pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { + if accounts.len() < 2 { + return Err(ProgramError::NotEnoughAccountKeys.into()); + } + + let fast_market_order = &accounts[0]; + let refund_recipient = &accounts[1]; + + if !refund_recipient.is_signer { + msg!("Refund recipient (account #2) is not a signer"); + return Err(ProgramError::InvalidAccountData.into()); + } + + let fast_market_order_data = FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + if fast_market_order_data.refund_recipient != refund_recipient.key().as_ref() { + msg!("Refund recipient (account #2) mismatch"); + msg!("Actual:"); + msg!("{:?}", refund_recipient.key.as_ref()); + msg!("Expected:"); + msg!("{:?}", fast_market_order_data.refund_recipient); + return Err(ProgramError::InvalidAccountData.into()); + } + + let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut(); + **refund_recipient.lamports.borrow_mut() += **fast_market_order_lamports; + **fast_market_order_lamports = 0; + + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/errors.rs b/solana/programs/matching-engine/src/fallback/processor/errors.rs index 677893013..3c813afd5 100644 --- a/solana/programs/matching-engine/src/fallback/processor/errors.rs +++ b/solana/programs/matching-engine/src/fallback/processor/errors.rs @@ -27,5 +27,8 @@ pub enum FallbackError { #[msg("Token transfer failed")] TokenTransferFailed, + #[msg("Invalid CCTP message")] + InvalidCctpMessage, + // Add more error variants as needed } \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 1e41b62ea..797421142 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,5 +1,5 @@ use anchor_lang::prelude::*; -use anchor_spl::token::spl_token; +use anchor_spl::token::{spl_token, TokenAccount}; use common::messages::Fill; use solana_program::program::invoke_signed_unchecked; use crate::utils; @@ -16,39 +16,70 @@ use crate::error::MatchingEngineError; #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExecuteOrderShimAccounts<'ix> { + /// The signer account pub signer: &'ix Pubkey, // 0 + /// The cctp message account. CHECK: Seeds must be \["cctp-msg", auction_address.as_ref()\]. pub cctp_message: &'ix Pubkey, // 1 + /// The custodian account of the auction (holds the best offer amount) pub custodian: &'ix Pubkey, // 2 + /// The fast market order account created from the place initial offer instruction + /// CHECK: Seeds must be \["fast_market_order", auction_address.as_ref()\]. pub fast_market_order: &'ix Pubkey, // 3 + /// The auction account created from the place initial offer instruction pub active_auction: &'ix Pubkey, // 4 + /// The associated token address of the auction's custody token pub active_auction_custody_token: &'ix Pubkey, // 5 + /// The auction config account created from the place initial offer instruction pub active_auction_config: &'ix Pubkey, // 6 + /// The token account of the auction's best offer pub active_auction_best_offer_token: &'ix Pubkey, // 7 + /// ??? pub executor_token: &'ix Pubkey, // 8 + /// The token account of the auction's initial offer pub initial_offer_token: &'ix Pubkey, // 9 + /// The account that signed the creation of the auction when placing the initial offer. pub initial_participant: &'ix Pubkey, // 10 + /// The router endpoint account of the auction's target chain pub to_router_endpoint: &'ix Pubkey, // 11 - // Add shim post message accounts. TODO: Ask about if these are needed at all, or if they can just be imported/derived + /// The program id of the post message shim program pub post_message_shim_program: &'ix Pubkey, // 12 + /// The sequence account of the post message shim program (can be derived) pub post_message_sequence: &'ix Pubkey, // 13 + /// The message account of the post message shim program (can be derived) pub post_message_message: &'ix Pubkey, // 14 + /// The mint account of the CCTP token to be burned pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 15 + /// The token messenger minter sender authority account of the CCTP token to be burned pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 16 + /// The message transmitter config account of the CCTP token to be burned pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 17 + /// The token messenger account of the CCTP token to be burned pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 18 + /// The remote token messenger account of the CCTP token to be burned pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 19 + /// The token minter account of the CCTP token to be burned pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 20 + /// The local token account of the CCTP token to be burned pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 21 + /// The token messenger minter event authority account of the CCTP token to be burned pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 22 + /// The token messenger minter program account of the CCTP token to be burned pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 23 + /// The message transmitter program account of the CCTP token to be burned pub cctp_deposit_for_burn_message_transmitter_program: &'ix Pubkey, // 24 - // Core bridge program accounts + /// The program id of the core bridge program pub core_bridge_program: &'ix Pubkey, // 25 + /// The config account of the core bridge program pub core_bridge_config: &'ix Pubkey, // 26 + /// The fee collector account of the core bridge program pub core_bridge_fee_collector: &'ix Pubkey, // 27 + /// The event authority account of the post message shim program pub post_message_shim_event_authority: &'ix Pubkey, // 28 + /// The program id of the system program pub system_program: &'ix Pubkey, // 29 + /// The program id of the token program pub token_program: &'ix Pubkey, // 30 + /// The clock account pub clock: &'ix Pubkey, // 31 } @@ -58,35 +89,35 @@ impl<'ix> ExecuteOrderShimAccounts<'ix> { AccountMeta::new(*self.signer, true), AccountMeta::new(*self.cctp_message, false), AccountMeta::new(*self.custodian, false), - AccountMeta::new(*self.fast_market_order, false), + AccountMeta::new_readonly(*self.fast_market_order, false), AccountMeta::new(*self.active_auction, false), AccountMeta::new(*self.active_auction_custody_token, false), - AccountMeta::new(*self.active_auction_config, false), + AccountMeta::new_readonly(*self.active_auction_config, false), AccountMeta::new(*self.active_auction_best_offer_token, false), AccountMeta::new(*self.executor_token, false), AccountMeta::new(*self.initial_offer_token, false), AccountMeta::new(*self.initial_participant, false), - AccountMeta::new(*self.to_router_endpoint, false), - AccountMeta::new(*self.post_message_shim_program, false), + AccountMeta::new_readonly(*self.to_router_endpoint, false), + AccountMeta::new_readonly(*self.post_message_shim_program, false), AccountMeta::new(*self.post_message_sequence, false), AccountMeta::new(*self.post_message_message, false), AccountMeta::new(*self.cctp_deposit_for_burn_mint, false), - AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, false), AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_config, false), - AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger, false), - AccountMeta::new(*self.cctp_deposit_for_burn_remote_token_messenger, false), - AccountMeta::new(*self.cctp_deposit_for_burn_token_minter, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_remote_token_messenger, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_minter, false), AccountMeta::new(*self.cctp_deposit_for_burn_local_token, false), - AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_event_authority, false), - AccountMeta::new(*self.cctp_deposit_for_burn_token_messenger_minter_program, false), - AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_program, false), - AccountMeta::new(*self.core_bridge_program, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_program, false), + AccountMeta::new_readonly(*self.cctp_deposit_for_burn_message_transmitter_program, false), + AccountMeta::new_readonly(*self.core_bridge_program, false), AccountMeta::new(*self.core_bridge_config, false), AccountMeta::new(*self.core_bridge_fee_collector, false), AccountMeta::new(*self.post_message_shim_event_authority, false), - AccountMeta::new(*self.system_program, false), - AccountMeta::new(*self.token_program, false), - AccountMeta::new(*self.clock, false), + AccountMeta::new_readonly(*self.system_program, false), + AccountMeta::new_readonly(*self.token_program, false), + AccountMeta::new_readonly(*self.clock, false), ] } } @@ -368,7 +399,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Keep track of the remaining amount in the custody token account. Whatever remains will go // to the executor. - let mut remaining_custodied_amount = active_auction_info.amount_in.saturating_sub(user_amount); + + let custody_token = TokenAccount::try_deserialize(&mut &active_auction_custody_token_account.data.borrow()[..])?; + let mut remaining_custodied_amount = custody_token.amount.saturating_sub(user_amount); // Offer price + security deposit was checked in placing the initial offer. let mut deposit_and_fee = active_auction_info @@ -376,12 +409,21 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .saturating_add(active_auction_info.security_deposit) .saturating_sub(user_reward); + msg!("Security deposit: {}", active_auction_info.security_deposit); + let penalized = penalty > 0; if penalized && active_auction_best_offer_token_account.key() != executor_token_account.key() { deposit_and_fee = deposit_and_fee.saturating_sub(penalty); } + // Need these seeds in order to transfer tokens and then set authority of auction custody token account to the custodian + let auction_signer_seeds = &[ + Auction::SEED_PREFIX, + active_auction.vaa_hash.as_ref(), + &[active_auction.bump], + ]; + // If the initial offer token account doesn't exist anymore, we have nowhere to send the // init auction fee. The executor will get these funds instead. // @@ -389,9 +431,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { if utils::checked_deserialize_token_account(initial_offer_token_account, &common::USDC_MINT) .is_some() { + msg!("Initial offer token account exists"); if active_auction_best_offer_token_account.key() != initial_offer_token_account.key() { // Pay the auction initiator their fee. - spl_token::instruction::transfer( + let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, &active_auction_custody_token_account.key(), &initial_offer_token_account.key(), @@ -399,7 +442,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &[], init_auction_fee, ).unwrap(); - + msg!("Sending init auction fee {} to initial offer token account", init_auction_fee); + invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; // Because the initial offer token was paid this fee, we account for it here. remaining_custodied_amount = remaining_custodied_amount.saturating_sub(init_auction_fee); @@ -408,6 +452,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { deposit_and_fee = deposit_and_fee .checked_add(init_auction_fee) .ok_or_else(|| MatchingEngineError::U64Overflow)?; + msg!("New deposit and fee: {}", deposit_and_fee); } } @@ -419,14 +464,16 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // NOTE: This will revert if the best offer token does not exist. But this will present // an opportunity for another executor to execute this order and take what the best // offer token would have received. - spl_token::instruction::transfer( + let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, &active_auction_custody_token_account.key(), &active_auction_best_offer_token_account.key(), &active_auction_account.key(), &[], - remaining_custodied_amount, + deposit_and_fee, ).unwrap(); + msg!("Sending deposit and fee amount {} to best offer token account", deposit_and_fee); + invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; } else { // Otherwise, send the deposit and fee to the best offer token. If the best offer token // doesn't exist at this point (which would be unusual), we will reserve these funds @@ -434,7 +481,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { if utils::checked_deserialize_token_account(active_auction_best_offer_token_account, &common::USDC_MINT) .is_some() { - spl_token::instruction::transfer( + let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, &active_auction_custody_token_account.key(), &active_auction_best_offer_token_account.key(), @@ -442,13 +489,15 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &[], deposit_and_fee, ).unwrap(); + msg!("Sending deposit and fee {} to best offer token account", deposit_and_fee); + invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; remaining_custodied_amount = remaining_custodied_amount.saturating_sub(deposit_and_fee); } // And pay the executor whatever remains in the auction custody token account. if remaining_custodied_amount > 0 { - spl_token::instruction::transfer( + let instruction = spl_token::instruction::transfer( &spl_token::ID, &active_auction_custody_token_account.key(), &executor_token_account.key(), @@ -456,6 +505,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &[], remaining_custodied_amount, ).unwrap(); + msg!("Sending remaining custodied amount {} to executor token account", remaining_custodied_amount); + invoke_signed_unchecked(&instruction, accounts, &[auction_signer_seeds])?; } } @@ -470,11 +521,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &[], ).unwrap(); - let auction_signer_seeds = &[ - Auction::SEED_PREFIX, - active_auction.vaa_hash.as_ref(), - &[active_auction.bump], - ]; invoke_signed_unchecked(&set_authority_ix, accounts, &[auction_signer_seeds])?; // Set the active auction status @@ -491,6 +537,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; let post_message_accounts = PostMessageAccounts::new(custodian_account.key(), signer_account.key()); + // Lets print the auction account balance + burn_and_post( CpiContext::new_with_signer( cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), @@ -537,7 +585,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Skip emitting the order executed event because we're using a shim // Finally close the account since it is no longer needed. - spl_token::instruction::close_account( + let instruction = spl_token::instruction::close_account( &spl_token::ID, &active_auction_custody_token_account.key(), &initial_participant_account.key(), @@ -545,5 +593,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &[], ).unwrap(); + invoke_signed_unchecked(&instruction, accounts, &[Custodian::SIGNER_SEEDS])?; + Ok(()) } \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs new file mode 100644 index 000000000..7f15c9c5c --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -0,0 +1,160 @@ +use anchor_lang::prelude::*; +use bytemuck::{Pod, Zeroable}; +use anchor_lang::Discriminator; +use solana_program::program::invoke_signed_unchecked; +use solana_program::instruction::Instruction; + +use super::create_account::create_account_reliably; + +use super::FallbackMatchingEngineInstruction; +use crate::state::FastMarketOrder as FastMarketOrderState; +use super::errors::FallbackError; + +pub struct InitialiseFastMarketOrderAccounts<'ix> { + pub signer: &'ix Pubkey, + pub fast_market_order_account: &'ix Pubkey, + pub guardian_set: &'ix Pubkey, + pub guardian_set_signatures: &'ix Pubkey, + pub verify_vaa_shim_program: &'ix Pubkey, + pub system_program: &'ix Pubkey, +} + +impl<'ix> InitialiseFastMarketOrderAccounts<'ix> { + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new(*self.signer, true), // This will be the refund recipient + AccountMeta::new(*self.fast_market_order_account, false), + AccountMeta::new_readonly(*self.guardian_set, false), + AccountMeta::new_readonly(*self.guardian_set_signatures, false), + AccountMeta::new(*self.verify_vaa_shim_program, false), + AccountMeta::new(*self.system_program, false), + ] + } +} + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct InitialiseFastMarketOrderData { + pub fast_market_order: FastMarketOrderState, + pub guardian_set_bump: u8, + _padding: [u8; 7], +} +impl InitialiseFastMarketOrderData { + pub fn new(fast_market_order: FastMarketOrderState, guardian_set_bump: u8) -> Self { + Self { fast_market_order, guardian_set_bump, _padding: [0_u8; 7] } + } + + pub fn from_bytes(data: &[u8]) -> Option<&Self> { + bytemuck::try_from_bytes::(data).ok() + } +} + +pub struct InitialiseFastMarketOrder<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: InitialiseFastMarketOrderAccounts<'ix>, + pub data: InitialiseFastMarketOrderData, +} + +impl InitialiseFastMarketOrder<'_> { + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(&self.data).to_vec(), + } + } +} + +pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseFastMarketOrderData) -> Result<()> { + if accounts.len() < 6 { + return Err(ErrorCode::AccountNotEnoughKeys.into()); + } + let signer = &accounts[0]; + let fast_market_order_account = &accounts[1]; + let guardian_set = &accounts[2]; + let guardian_set_signatures = &accounts[3]; + let _verify_vaa_shim_program = &accounts[4]; + let _system_program = &accounts[5]; + + let InitialiseFastMarketOrderData { fast_market_order, guardian_set_bump, _padding: _ } = *data; + // Start of cpi call to verify the shim. + // ------------------------------------------------------------------------------------------------ + + // Did not want to pass in the vaa hash here. So recreated it. + let verify_hash_data = { + let mut data = vec![]; + data.extend_from_slice(&wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR); + data.push(guardian_set_bump); + data.extend_from_slice(&fast_market_order.digest); + data + }; + let verify_shim_ix = Instruction { + program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: vec![ + AccountMeta::new_readonly(guardian_set.key(), false), + AccountMeta::new_readonly(guardian_set_signatures.key(), false), + ], + data: verify_hash_data, + }; + // Make the cpi call to verify the shim. + invoke_signed_unchecked(&verify_shim_ix, &[ + guardian_set.to_account_info(), + guardian_set_signatures.to_account_info(), + ], &[])?; + // ------------------------------------------------------------------------------------------------ + // End of cpi call to verify the shim. + + let fast_market_order_key = fast_market_order_account.key(); + // Create the fast market order account + let program_id = crate::ID; + let space = 8 + std::mem::size_of::(); + let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + fast_market_order.digest.as_ref(), + fast_market_order.refund_recipient.as_ref(), + ], + &program_id, + ); + + if fast_market_order_pda != fast_market_order_key { + msg!("Fast market order pda is invalid"); + return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); + } + let fast_market_order_seeds = [ + FastMarketOrderState::SEED_PREFIX, + fast_market_order.digest.as_ref(), + fast_market_order.refund_recipient.as_ref(), + &[fast_market_order_bump], + ]; + let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; + // Create the account using the system program + create_account_reliably( + &signer.key(), + &fast_market_order_key, + fast_market_order_account.lamports(), + space, + accounts, + &program_id, + fast_market_order_signer_seeds, + )?; + + // Borrow the account data mutably + let mut fast_market_order_account_data = fast_market_order_account.try_borrow_mut_data()?; + + // Write the discriminator to the first 8 bytes + let discriminator = FastMarketOrderState::discriminator(); + fast_market_order_account_data[0..8].copy_from_slice(&discriminator); + + let fast_market_order_bytes = bytemuck::bytes_of(&data.fast_market_order); + // Ensure the destination has enough space + if fast_market_order_account_data.len() < 8 + fast_market_order_bytes.len() { + msg!("Account data buffer too small"); + return Err(FallbackError::AccountDataTooSmall.into()); + } + + // Write the fast_market_order struct to the account + fast_market_order_account_data[8..8 + fast_market_order_bytes.len()].copy_from_slice(fast_market_order_bytes); + + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 22ab72212..6838e466d 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -4,4 +4,7 @@ pub mod errors; pub mod create_account; pub mod place_initial_offer; pub mod execute_order; -pub mod burn_and_post; \ No newline at end of file +pub mod burn_and_post; +pub mod prepare_order_response; +pub mod initialise_fast_market_order; +pub mod close_fast_market_order; \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 449c0fd83..6f7eb3c62 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -7,7 +7,6 @@ use super::create_account::create_account_reliably; use solana_program::keccak; use anchor_lang::Discriminator; use solana_program::program_pack::Pack; -use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashAccounts, VerifyHashData}; use crate::state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use crate::ID as PROGRAM_ID; @@ -22,16 +21,14 @@ pub struct PlaceInitialOfferCctpShimData { pub offer_price: u64, pub sequence: u64, pub vaa_time: u32, - pub guardian_set_bump: u8, pub consistency_level: u8, - _padding: [u8; 2], - pub fast_market_order: FastMarketOrderState, + _padding: [u8; 3], } impl PlaceInitialOfferCctpShimData { - pub fn new(offer_price: u64, sequence: u64, vaa_time: u32, guardian_set_bump: u8, consistency_level: u8, fast_market_order: FastMarketOrderState) -> Self { - Self { offer_price, sequence, vaa_time, guardian_set_bump, consistency_level, _padding: [0_u8; 2], fast_market_order } + pub fn new(offer_price: u64, sequence: u64, vaa_time: u32, consistency_level: u8) -> Self { + Self { offer_price, sequence, vaa_time, consistency_level, _padding: [0_u8; 3] } } pub fn from_bytes(data: &[u8]) -> Option<&Self> { @@ -45,8 +42,6 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub transfer_authority: &'ix Pubkey, pub custodian: &'ix Pubkey, pub auction_config: &'ix Pubkey, - pub guardian_set: &'ix Pubkey, - pub guardian_set_signatures: &'ix Pubkey, pub from_endpoint: &'ix Pubkey, pub to_endpoint: &'ix Pubkey, pub fast_market_order: &'ix Pubkey, // Needs initalising. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] @@ -54,7 +49,6 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub offer_token: &'ix Pubkey, pub auction_custody_token: &'ix Pubkey, pub usdc: &'ix Pubkey, - pub verify_vaa_shim_program: &'ix Pubkey, pub system_program: &'ix Pubkey, pub token_program: &'ix Pubkey, } @@ -62,23 +56,19 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { impl<'ix> PlaceInitialOfferCctpShimAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ - // TODO: Change some to read only using the new_readonly AccountMeta::new(*self.signer, true), - AccountMeta::new(*self.transfer_authority, false), - AccountMeta::new(*self.custodian, false), - AccountMeta::new(*self.auction_config, false), - AccountMeta::new(*self.guardian_set, false), - AccountMeta::new(*self.guardian_set_signatures, false), - AccountMeta::new(*self.from_endpoint, false), - AccountMeta::new(*self.to_endpoint, false), + AccountMeta::new_readonly(*self.transfer_authority, false), + AccountMeta::new_readonly(*self.custodian, false), + AccountMeta::new_readonly(*self.auction_config, false), + AccountMeta::new_readonly(*self.from_endpoint, false), + AccountMeta::new_readonly(*self.to_endpoint, false), AccountMeta::new(*self.fast_market_order, false), AccountMeta::new(*self.auction, false), AccountMeta::new(*self.offer_token, false), AccountMeta::new(*self.auction_custody_token, false), - AccountMeta::new(*self.usdc, false), - AccountMeta::new(*self.verify_vaa_shim_program, false), - AccountMeta::new(*self.system_program, false), - AccountMeta::new(*self.token_program, false), + AccountMeta::new_readonly(*self.usdc, false), + AccountMeta::new_readonly(*self.system_program, false), + AccountMeta::new_readonly(*self.token_program, false), ] } } @@ -170,38 +160,36 @@ impl VaaMessageBodyHeader { pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { // Check account owners let program_id = &crate::ID; // Your program ID + // Check all accounts are valid - if accounts.len() < 16 { + if accounts.len() < 11 { return Err(ErrorCode::AccountNotEnoughKeys.into()); } // Extract data fields + // TODO: Remove sequence, vaa_time because they are in the fast market order state let PlaceInitialOfferCctpShimData { offer_price, - guardian_set_bump, sequence, vaa_time, consistency_level, _padding, - fast_market_order: fast_market_order_zero_copy, } = *data; let signer = &accounts[0]; let transfer_authority = &accounts[1]; let custodian = &accounts[2]; let auction_config = &accounts[3]; - let guardian_set = &accounts[4]; - let guardian_set_signatures = &accounts[5]; - let from_endpoint = &accounts[6]; - let to_endpoint = &accounts[7]; - let fast_market_order_account = &accounts[8]; - let auction_account = &accounts[9]; + let from_endpoint = &accounts[4]; + let to_endpoint = &accounts[5]; + let fast_market_order_account = &accounts[6]; + let auction_account = &accounts[7]; let auction_key = auction_account.key(); - let offer_token = &accounts[10]; - let auction_custody_token = &accounts[11]; - let usdc = &accounts[12]; - // let verify_vaa_shim_program = &accounts[13]; - // let system_program = &accounts[14]; - // let token_program = &accounts[15]; + let offer_token = &accounts[8]; + let auction_custody_token = &accounts[9]; + let usdc = &accounts[10]; + + + let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; // Check pda of the transfer authority is valid let transfer_authority_seeds = [ @@ -292,12 +280,6 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti msg!("To endpoint chain is not equal to the fast_market_order target_chain"); return Err(MatchingEngineError::InvalidTargetRouter.into()); } - - // Check if the fast_market_order account is already initialized - if !fast_market_order_account.data_is_empty() { - msg!("Fast market order account is already initialized"); - return Err(FallbackError::AccountAlreadyInitialized.into()); - } // Check contents of fast_market_order { @@ -314,7 +296,6 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti return Err(MatchingEngineError::OfferPriceTooHigh.into()); } } - let fast_market_order_key = fast_market_order_account.key(); // Create the vaa_message struct to get the digest let vaa_message = VaaMessageBodyHeader::new(consistency_level, vaa_time, sequence, from_endpoint_account.chain, from_endpoint_account.address); @@ -336,7 +317,6 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti &[auction_custody_token_bump], ]; let auction_custody_token_signer_seeds = &[&auction_custody_token_seeds[..]]; - // TODO: Must use create_account_reliably create_account_reliably( &signer.key(), &auction_custody_token_pda, @@ -362,56 +342,6 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti // ------------------------------------------------------------------------------------------------ // End of initialisation of auction custody token account - // Begin of initialisation of fast_market_order account - // ------------------------------------------------------------------------------------------------ - let space = 8 + std::mem::size_of::(); - let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( - &[ - FastMarketOrderState::SEED_PREFIX, - auction_key.as_ref(), - ], - &program_id, - ); - - if fast_market_order_pda != fast_market_order_key { - msg!("Fast market order pda is invalid"); - return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); - } - let fast_market_order_seeds = [ - FastMarketOrderState::SEED_PREFIX, - auction_key.as_ref(), - &[fast_market_order_bump], - ]; - let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; - // Create the account using the system program - create_account_reliably( - &signer.key(), - &fast_market_order_key, - fast_market_order_account.lamports(), - space, - accounts, - &program_id, - fast_market_order_signer_seeds, - )?; - // Borrow the account data mutably - let mut data = fast_market_order_account.try_borrow_mut_data()?; - - // Write the discriminator to the first 8 bytes - let discriminator = FastMarketOrderState::discriminator(); - data[0..8].copy_from_slice(&discriminator); - - let fast_market_order_bytes = bytemuck::bytes_of(&fast_market_order_zero_copy); - - // Ensure the destination has enough space - if data.len() < 8 + fast_market_order_bytes.len() { - msg!("Account data buffer too small"); - return Err(FallbackError::AccountDataTooSmall.into()); - } - - // Write the fast_market_order struct to the account - data[8..8 + fast_market_order_bytes.len()].copy_from_slice(fast_market_order_bytes); - // ------------------------------------------------------------------------------------------------ - // End of initialisation of fast_market_order account // Begin of initialisation of auction account // ------------------------------------------------------------------------------------------------ @@ -484,25 +414,6 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti data[8..8 + auction_bytes.len()].copy_from_slice(&auction_bytes); // ------------------------------------------------------------------------------------------------ // End of initialisation of auction account - - // Start of cpi call to verify the shim. - // ------------------------------------------------------------------------------------------------ - let verify_hash_data = VerifyHashData::new(guardian_set_bump, vaa_message_digest.clone()); - let verify_shim_ix = VerifyHash { - program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: VerifyHashAccounts { - guardian_set: &guardian_set.key(), - guardian_signatures: &guardian_set_signatures.key(), - }, - data: verify_hash_data - }.instruction(); - // Make the cpi call to verify the shim. - invoke_signed_unchecked(&verify_shim_ix, &[ - guardian_set.to_account_info(), - guardian_set_signatures.to_account_info(), - ], &[])?; - // ------------------------------------------------------------------------------------------------ - // End of cpi call to verify the shim. // Start of token transfer from offer token to auction custody token // ------------------------------------------------------------------------------------------------ @@ -534,19 +445,26 @@ mod tests { #[test] fn test_bytemuck() { - let test_fast_market_order = FastMarketOrderState { - amount_in: 1000000000000000000, - min_amount_out: 1000000000000000000, - deadline: 1000000000, - target_chain: 1, - redeemer_message_length: 0, - redeemer: [0_u8; 32], - sender: [0_u8; 32], - refund_address: [0_u8; 32], - max_fee: 0, - init_auction_fee: 0, - redeemer_message: [0_u8; 512], - }; + let test_fast_market_order = FastMarketOrderState::new( + 1000000000000000000, + 1000000000000000000, + 1000000000, + 1, + 0, + [0_u8; 32], + [0_u8; 32], + [0_u8; 32], + 0, + 0, + [0_u8; 512], + [0_u8; 32], + [0_u8; 32], + 0, + 0, + 0, + [0_u8; 32], + + ); let bytes = bytemuck::bytes_of(&test_fast_market_order); assert!(bytes.len() == std::mem::size_of::()); } diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs new file mode 100644 index 000000000..e051a5320 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -0,0 +1,386 @@ +use std::io::Cursor; + +use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; +use common::messages::raw::SlowOrderResponse; +use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; +use common::wormhole_cctp_solana::utils::CctpMessage; +use solana_program::program::invoke_signed_unchecked; +use super::create_account::create_account_reliably; +use solana_program::instruction::Instruction; +use crate::state::PreparedOrderResponseInfo; +use crate::state::PreparedOrderResponseSeeds; +use crate::state::{Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, PreparedOrderResponse, RouterEndpoint}; +use common::wormhole_cctp_solana::cctp::message_transmitter_program; + +use super::errors::FallbackError; +use crate::error::MatchingEngineError; + + +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct PrepareOrderResponseCctpShimData { + pub encoded_cctp_message: Vec, + pub cctp_attestation: Vec, + pub deposit_payload: Vec, +} + +impl PrepareOrderResponseCctpShimData { + pub fn new(encoded_cctp_message: Vec, cctp_attestation: Vec, deposit_payload: Vec) -> Self { + Self { encoded_cctp_message, cctp_attestation, deposit_payload } + } + pub fn from_bytes(data: &[u8]) -> Option { + Self::try_from_slice(data).ok() + } + pub fn to_bytes(&self) -> Vec { + self.try_to_vec().unwrap() + } + + pub fn to_receive_message_args(&self) -> ReceiveMessageArgs { + let mut encoded_message = Vec::with_capacity(self.encoded_cctp_message.len()); + encoded_message.extend_from_slice(&self.encoded_cctp_message); + let mut cctp_attestation = Vec::with_capacity(self.cctp_attestation.len()); + cctp_attestation.extend_from_slice(&self.cctp_attestation); + ReceiveMessageArgs { encoded_message, attestation: cctp_attestation } + } +} + +pub struct PrepareOrderResponseCctpShimAccounts<'ix> { + pub signer: &'ix Pubkey, + pub custodian: &'ix Pubkey, + pub fast_market_order: &'ix Pubkey, + pub from_endpoint: &'ix Pubkey, + pub to_endpoint: &'ix Pubkey, + pub prepared_order_response: &'ix Pubkey, + pub prepared_custody_token: &'ix Pubkey, + pub base_fee_token: &'ix Pubkey, + pub usdc: &'ix Pubkey, + pub cctp_mint_recipient: &'ix Pubkey, + pub cctp_message_transmitter_authority: &'ix Pubkey, + pub cctp_message_transmitter_config: &'ix Pubkey, + pub cctp_used_nonces: &'ix Pubkey, + pub cctp_message_transmitter_event_authority: &'ix Pubkey, + pub cctp_token_messenger: &'ix Pubkey, + pub cctp_remote_token_messenger: &'ix Pubkey, + pub cctp_token_minter: &'ix Pubkey, + pub cctp_local_token: &'ix Pubkey, + pub cctp_token_pair: &'ix Pubkey, + pub cctp_token_messenger_minter_custody_token: &'ix Pubkey, + pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, + pub cctp_token_messenger_minter_program: &'ix Pubkey, + pub cctp_message_transmitter_program: &'ix Pubkey, + pub token_program: &'ix Pubkey, + pub system_program: &'ix Pubkey, +} + +impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { + pub fn new(signer: &'ix Pubkey, custodian: &'ix Pubkey, fast_market_order: &'ix Pubkey, from_endpoint: &'ix Pubkey, to_endpoint: &'ix Pubkey, prepared_order_response: &'ix Pubkey, prepared_custody_token: &'ix Pubkey, base_fee_token: &'ix Pubkey, usdc: &'ix Pubkey, cctp_mint_recipient: &'ix Pubkey, cctp_message_transmitter_authority: &'ix Pubkey, cctp_message_transmitter_config: &'ix Pubkey, cctp_used_nonces: &'ix Pubkey, cctp_message_transmitter_event_authority: &'ix Pubkey, cctp_token_messenger: &'ix Pubkey, cctp_remote_token_messenger: &'ix Pubkey, cctp_token_minter: &'ix Pubkey, cctp_local_token: &'ix Pubkey, cctp_token_pair: &'ix Pubkey, cctp_token_messenger_minter_custody_token: &'ix Pubkey, cctp_token_messenger_minter_event_authority: &'ix Pubkey, cctp_token_messenger_minter_program: &'ix Pubkey, cctp_message_transmitter_program: &'ix Pubkey, token_program: &'ix Pubkey, system_program: &'ix Pubkey) -> Self { + Self { signer, custodian, fast_market_order, from_endpoint, to_endpoint, prepared_order_response, prepared_custody_token, base_fee_token, usdc, cctp_mint_recipient, cctp_local_token, cctp_message_transmitter_authority, cctp_message_transmitter_config, cctp_message_transmitter_event_authority, cctp_message_transmitter_program, cctp_remote_token_messenger, cctp_token_messenger, cctp_token_messenger_minter_custody_token, cctp_token_messenger_minter_event_authority, cctp_token_messenger_minter_program, cctp_token_minter, cctp_token_pair, cctp_used_nonces, token_program, system_program } + } + + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new(*self.signer, false), + AccountMeta::new_readonly(*self.custodian, false), + AccountMeta::new_readonly(*self.fast_market_order, false), + AccountMeta::new_readonly(*self.from_endpoint, false), + AccountMeta::new_readonly(*self.to_endpoint, false), + AccountMeta::new_readonly(*self.prepared_order_response, false), + AccountMeta::new_readonly(*self.prepared_custody_token, false), + AccountMeta::new_readonly(*self.base_fee_token, false), + AccountMeta::new_readonly(*self.usdc, false), + AccountMeta::new_readonly(*self.cctp_mint_recipient, false), + AccountMeta::new_readonly(*self.cctp_message_transmitter_authority, false), + AccountMeta::new_readonly(*self.cctp_message_transmitter_config, false), + AccountMeta::new(*self.cctp_used_nonces, false), + AccountMeta::new_readonly(*self.cctp_message_transmitter_event_authority, false), + AccountMeta::new_readonly(*self.cctp_token_messenger, false), + AccountMeta::new_readonly(*self.cctp_remote_token_messenger, false), + AccountMeta::new_readonly(*self.cctp_token_minter, false), + AccountMeta::new(*self.cctp_local_token, false), + AccountMeta::new_readonly(*self.cctp_token_pair, false), + AccountMeta::new(*self.cctp_token_messenger_minter_custody_token, false), + AccountMeta::new_readonly(*self.cctp_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*self.cctp_token_messenger_minter_program, false), + AccountMeta::new_readonly(*self.cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*self.token_program, false), + AccountMeta::new_readonly(*self.system_program, false), + ] + } +} + +pub struct PrepareOrderResponseCctpShim<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: PrepareOrderResponseCctpShimAccounts<'ix>, + pub data: PrepareOrderResponseCctpShimData, +} + +impl<'ix> PrepareOrderResponseCctpShim<'ix> { + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: self.data.to_bytes(), + } + } +} + +pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData) -> Result<()> { + let program_id = &crate::ID; + if accounts.len() < 24 { + return Err(ErrorCode::AccountNotEnoughKeys.into()); + } + let signer = &accounts[0]; + let custodian = &accounts[1]; + let fast_market_order = &accounts[2]; + let from_endpoint = &accounts[3]; + let to_endpoint = &accounts[4]; + let prepared_order_response = &accounts[5]; + let prepared_custody_token = &accounts[6]; + let base_fee_token = &accounts[7]; + let usdc = &accounts[8]; + let cctp_mint_recipient = &accounts[9]; + let cctp_message_transmitter_authority = &accounts[10]; + let cctp_message_transmitter_config = &accounts[11]; + let cctp_used_nonces = &accounts[12]; + let cctp_message_transmitter_event_authority = &accounts[13]; + let cctp_token_messenger = &accounts[14]; + let cctp_remote_token_messenger = &accounts[15]; + let cctp_token_minter = &accounts[16]; + let cctp_local_token = &accounts[17]; + let cctp_token_pair = &accounts[18]; + let cctp_token_messenger_minter_custody_token = &accounts[19]; + let cctp_token_messenger_minter_event_authority = &accounts[20]; + let cctp_token_messenger_minter_program = &accounts[21]; + let cctp_message_transmitter_program = &accounts[22]; + let token_program = &accounts[23]; + let system_program = &accounts[24]; + + // Check that fast market order is owned by the program + if fast_market_order.owner != program_id { + msg!("Fast market order owner is invalid: expected {}, got {}", program_id, fast_market_order.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + + // Load accounts + let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + // Load from cctp message. + let cctp_message = CctpMessage::parse(&data.encoded_cctp_message).map_err(|_| FallbackError::InvalidCctpMessage)?; + let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; + // Deserialise the to_endpoint account + let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; + // Deserialise the from_endpoint account + let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; + + // Check custodian account + if custodian.owner != program_id { + msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + } + + if checked_custodian.paused { + msg!("Custodian is paused"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("custodian")); + } + + // Check usdc mint + if usdc.key() != common::USDC_MINT { + msg!("Usdc mint is invalid"); + return Err(FallbackError::InvalidMint.into()); + } + + // Check from_endpoint owner + if from_endpoint.owner != program_id { + msg!("From endpoint owner is invalid: expected {}, got {}", program_id, from_endpoint.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("from_endpoint")); + } + + // Check to_endpoint owner + if to_endpoint.owner != program_id { + msg!("To endpoint owner is invalid: expected {}, got {}", program_id, to_endpoint.owner); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("to_endpoint")); + } + + + // Check that the from and to endpoints are different + if from_endpoint_account.chain == to_endpoint_account.chain { + return Err(MatchingEngineError::SameEndpoint.into()); + } + + // Check that the to endpoint protocol is cctp or local + match to_endpoint_account.protocol { + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), + _ => return Err(MatchingEngineError::InvalidEndpoint.into()), + } + + // Check that the from endpoint protocol is cctp or local + match from_endpoint_account.protocol { + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), + _ => return Err(MatchingEngineError::InvalidEndpoint.into()), + } + + // Check that to endpoint chain is equal to the fast_market_order target_chain + if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { + msg!("To endpoint chain is not equal to the fast_market_order target_chain"); + return Err(MatchingEngineError::InvalidTargetRouter.into()); + } + + // Check the prepared order response account is valid + let prepared_order_response_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_zero_copy.digest + ]; + + let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, program_id); + + if prepared_order_response_pda != prepared_order_response.key() { + msg!("Prepared order response pda is invalid"); + return Err(FallbackError::InvalidPda.into()) + .map_err(|e: Error| e.with_account_name("prepared_order_response")); + } + + // TODO: Figure out how to convert source domain to emitter chain + // Check vaa emitter chain matches fast market order emitter chain + if fast_market_order_zero_copy.vaa_emitter_chain != cctp_message.source_domain() as u16 { + msg!("Vaa emitter chain does not match fast market order emitter chain"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + // TODO: Figure out how to find emitter address to check against + + // Check vaa emitter address matches fast market order emitter address + if fast_market_order_zero_copy.sender != cctp_message.sender() { + msg!("Vaa emitter address does not match fast market order emitter address"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + + // TODO: Figure out how to check the sequence number + // if fast_market_order_zero_copy.vaa_sequence != cctp_message.sequence().saturating_add(1) { + // msg!("Vaa sequence must be exactly 1 greater than fast market order sequence"); + // return Err(MatchingEngineError::VaaMismatch.into()) + // .map_err(|e: Error| e.with_account_name("fast_market_order")); + // } + + // TODO: Figure out how to check the timestamp + // if fast_market_order_zero_copy.vaa_timestamp != cctp_message.timestamp() { + // msg!("Vaa timestamp does not match fast market order timestamp"); + // return Err(MatchingEngineError::VaaMismatch.into()) + // .map_err(|e: Error| e.with_account_name("fast_market_order")); + // } + + // Check loaded vaa is deposit message + let slow_order_response = SlowOrderResponse::parse(&data.deposit_payload).map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; + + // Check the base token fee key is not equal to the prepared custody token key + if base_fee_token.key() == prepared_custody_token.key() { + msg!("Base token fee key is equal to the prepared custody token key"); + return Err(MatchingEngineError::InvalidBaseFeeToken.into()) + .map_err(|e: Error| e.with_account_name("base_fee_token")); + } + + // Start create prepared order response account + // ------------------------------------------------------------------------------------------------ + + // Write to the prepared slow order account, which will be closed by one of the following + // instructions: + // * settle_auction_active_cctp + // * settle_auction_complete + // * settle_auction_none + let create_prepared_order_respone_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_zero_copy.digest, + &[prepared_order_response_bump], + ]; + let prepared_order_response_signer_seeds = &[&create_prepared_order_respone_seeds[..]]; + let prepared_order_response_account_space = PreparedOrderResponse::compute_size(fast_market_order_zero_copy.redeemer_message_length.into()); + + create_account_reliably( + &signer.key(), + &prepared_order_response.key(), + prepared_order_response.lamports(), + prepared_order_response_account_space, + accounts, + program_id, + prepared_order_response_signer_seeds, + )?; + + // Write the prepared order response account data ... + let prepared_order_response_account_to_write = PreparedOrderResponse { + seeds: PreparedOrderResponseSeeds { + fast_vaa_hash: fast_market_order_zero_copy.digest, + bump: prepared_order_response_bump, + }, + info: PreparedOrderResponseInfo { + prepared_by: signer.key(), + base_fee_token: base_fee_token.key(), + source_chain: fast_market_order_zero_copy.vaa_emitter_chain, + base_fee: slow_order_response.base_fee(), + fast_vaa_timestamp: fast_market_order_zero_copy.vaa_timestamp, + amount_in: fast_market_order_zero_copy.amount_in, + sender: fast_market_order_zero_copy.sender, + redeemer: fast_market_order_zero_copy.redeemer, + init_auction_fee: fast_market_order_zero_copy.init_auction_fee, + }, + to_endpoint: to_endpoint_account.info, + redeemer_message: fast_market_order_zero_copy.redeemer_message[..fast_market_order_zero_copy.redeemer_message_length as usize].to_vec(), + }; + // Use cursor in order to write the prepared order response account data + let prepared_order_response_data: &mut [u8] = &mut prepared_order_response.try_borrow_mut_data().map_err(|_| FallbackError::AccountNotWritable)?; + let mut cursor = Cursor::new(prepared_order_response_data); + prepared_order_response_account_to_write.try_serialize(&mut cursor).map_err(|_| FallbackError::BorshDeserializationError)?; + + // End create prepared order response account + // ------------------------------------------------------------------------------------------------ + + // Create cpi context for verify_vaa_and_mint + message_transmitter_program::cpi::receive_token_messenger_minter_message( + CpiContext::new_with_signer( + cctp_message_transmitter_program.to_account_info(), + message_transmitter_program::cpi::ReceiveTokenMessengerMinterMessage { + payer: signer.to_account_info(), + caller: custodian.to_account_info(), + message_transmitter_authority: cctp_message_transmitter_authority.to_account_info(), + message_transmitter_config: cctp_message_transmitter_config.to_account_info(), + used_nonces: cctp_used_nonces.to_account_info(), + token_messenger_minter_program: cctp_token_messenger_minter_program.to_account_info(), + system_program: system_program.to_account_info(), + message_transmitter_event_authority: cctp_message_transmitter_event_authority.to_account_info(), + message_transmitter_program: cctp_message_transmitter_program.to_account_info(), + token_messenger: cctp_token_messenger.to_account_info(), + remote_token_messenger: cctp_remote_token_messenger.to_account_info(), + token_minter: cctp_token_minter.to_account_info(), + local_token: cctp_local_token.to_account_info(), + token_pair: cctp_token_pair.to_account_info(), + mint_recipient: cctp_mint_recipient.to_account_info(), + custody_token: cctp_token_messenger_minter_custody_token.to_account_info(), + token_program: token_program.to_account_info(), + token_messenger_minter_event_authority: cctp_token_messenger_minter_event_authority.to_account_info(), + }, + &[Custodian::SIGNER_SEEDS], + ), + data.to_receive_message_args(), + )?; + + // Finally transfer minted via CCTP to prepared custody token. + let transfer_ix = spl_token::instruction::transfer( + &spl_token::ID, + &cctp_mint_recipient.key(), + &prepared_custody_token.key(), + &cctp_message_transmitter_authority.key(), + &[], // Apparently this is only for multi-sig accounts + fast_market_order_zero_copy.amount_in, + ).unwrap(); + + invoke_signed_unchecked(&transfer_ix, accounts, &[ + Custodian::SIGNER_SEEDS, + ]).map_err(|_| FallbackError::TokenTransferFailed)?; + + Ok(()) +} + + diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 090dad76b..8cc470266 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -5,17 +5,27 @@ use wormhole_svm_definitions::make_anchor_discriminator; use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::place_initial_offer::place_initial_offer_cctp_shim; use super::execute_order::handle_execute_order_shim; +use super::initialise_fast_market_order::{initialise_fast_market_order, InitialiseFastMarketOrderData}; +use super::close_fast_market_order::close_fast_market_order; +use super::prepare_order_response::prepare_order_response_cctp_shim; +use super::prepare_order_response::PrepareOrderResponseCctpShimData; impl<'ix> FallbackMatchingEngineInstruction<'ix> { pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:execute_order_cctp_shim"); + pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:initialise_fast_market_order"); + pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:close_fast_market_order"); + pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); } pub enum FallbackMatchingEngineInstruction<'ix> { PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, + InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), + CloseFastMarketOrder, + PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { @@ -30,6 +40,15 @@ pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instru }, FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { handle_execute_order_shim(accounts) + }, + FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(data) => { + initialise_fast_market_order(accounts, &data) + }, + FallbackMatchingEngineInstruction::CloseFastMarketOrder => { + close_fast_market_order(accounts) + } + FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { + prepare_order_response_cctp_shim(accounts, data) } } } @@ -47,6 +66,9 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { Some(Self::ExecuteOrderCctpShim) }, + FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => { + Some(Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..]))) + }, _ => None, } } @@ -79,6 +101,38 @@ impl FallbackMatchingEngineInstruction<'_> { out.extend_from_slice(&FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR); + out + } + Self::InitialiseFastMarketOrder(data) => { + let data_slice = bytemuck::bytes_of(*data); + let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + + let mut out = Vec::with_capacity(total_capacity); + + out.extend_from_slice(&FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR); + out.extend_from_slice(data_slice); + + out + }, + Self::CloseFastMarketOrder => { + let total_capacity = 8; // 8 for the selector (no data) + + let mut out = Vec::with_capacity(total_capacity); + + out.extend_from_slice(&FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR); + + out + }, + + Self::PrepareOrderResponseCctpShim(data) => { + let data_slice = data.to_bytes(); + let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + + let mut out = Vec::with_capacity(total_capacity); + + out.extend_from_slice(&FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR); + out.extend_from_slice(&data_slice); + out } } diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 6194e9d80..72fa5404c 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -489,6 +489,7 @@ pub mod matching_engine { pub fn fallback_process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { fallback::process_instruction(program_id, accounts, instruction_data) } + } #[derive(Accounts)] diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index b1660e076..57fd14b37 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -15,9 +15,20 @@ pub struct FastMarketOrder { pub max_fee: u64, pub init_auction_fee: u64, pub redeemer_message: [u8; 512], + pub digest: [u8; 32], + pub refund_recipient: [u8; 32], + pub vaa_sequence: u64, + pub vaa_timestamp: u32, + pub vaa_emitter_chain: u16, + pub vaa_emitter_address: [u8; 32], + _padding: [u8; 2], } impl FastMarketOrder { + pub fn new(amount_in: u64, min_amount_out: u64, deadline: u32, target_chain: u16, redeemer_message_length: u16, redeemer: [u8; 32], sender: [u8; 32], refund_address: [u8; 32], max_fee: u64, init_auction_fee: u64, redeemer_message: [u8; 512], digest: [u8; 32], refund_recipient: [u8; 32], vaa_sequence: u64, vaa_timestamp: u32, vaa_emitter_chain: u16, vaa_emitter_address: [u8; 32]) -> Self { + Self { amount_in, min_amount_out, deadline, target_chain, redeemer_message_length, redeemer, sender, refund_address, max_fee, init_auction_fee, redeemer_message, digest, refund_recipient, vaa_sequence, vaa_timestamp, vaa_emitter_chain, vaa_emitter_address, _padding: [0_u8; 2], } + } + pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; pub fn to_vec(&self) -> Vec { diff --git a/solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so b/solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so index 37df291820197a79c746f8b540710dd2aacf1364..340b873e8f4ca722d8292bcc1d8e3f53659dcb16 100755 GIT binary patch delta 19992 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 Z8c}|o2qy0)b4U4f(IsD~-daS){y)BYpHTn+ diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 58f60af10..9c833d666 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -12,8 +12,9 @@ use utils::setup::{PreTestingContext, TestingContext}; use utils::vaa::create_vaas_test_with_chain_and_address; use utils::shims::{ // place_initial_offer_shim, - place_initial_offer_fallback, set_up_post_message_transaction_test}; + place_initial_offer_fallback, set_up_post_message_transaction_test, initialise_fast_market_order_fallback_instruction}; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; + // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { @@ -159,11 +160,40 @@ pub async fn test_post_message_shims() { set_up_post_message_transaction_test(&testing_context.test_context, &payer_signer, &emitter_signer, recent_blockhash).await; } +#[tokio::test] +pub async fn test_initialise_fast_market_order_fallback() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + + // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let first_test_ft = vaas_test.0.first().unwrap(); + let solver = testing_context.testing_actors.solvers[0].clone(); + + let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; + + let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + &testing_context.testing_actors.owner.keypair(), + &PROGRAM_ID, + fast_market_order, + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ); + let recent_blockhash = testing_context.test_context.borrow().last_blockhash; + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer(&[initialise_fast_market_order_ix], Some(&testing_context.testing_actors.owner.pubkey()), &[&testing_context.testing_actors.owner.keypair()], recent_blockhash); + testing_context.test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to initialise fast market order"); +} #[tokio::test] // Testing a initial offer from arbitrum to ethereum // TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction -pub async fn test_verify_shims_fallback() { +pub async fn test_place_initial_offer_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); pre_testing_context.add_verify_shims(); // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. @@ -174,12 +204,14 @@ pub async fn test_verify_shims_fallback() { let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let first_test_ft = vaas_test.0.first().unwrap(); let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction let usdc_mint_address = USDC_MINT_ADDRESS; let auction_config_address = initialize_fixture.get_auction_config_address(); + println!("Creating router endpoints"); let router_endpoints = create_all_router_endpoints_test( &testing_context.test_context, testing_context.testing_actors.owner.pubkey(), @@ -190,6 +222,7 @@ pub async fn test_verify_shims_fallback() { testing_context.testing_actors.owner.keypair(), PROGRAM_ID, ).await; + println!("Router endpoints created"); let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; @@ -207,6 +240,7 @@ pub async fn test_verify_shims_fallback() { let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; // Place initial offer using the fallback program + println!("Placing initial offer"); let initial_offer_fixture = place_initial_offer_fallback( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), @@ -217,7 +251,7 @@ pub async fn test_verify_shims_fallback() { &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) ).await.expect("Failed to place initial offer"); - + println!("Initial offer placed"); let auction_offer_fixture = AuctionOfferFixture { auction_address: initial_offer_fixture.auction_address, auction_custody_token_address: initial_offer_fixture.auction_custody_token_address, @@ -225,7 +259,9 @@ pub async fn test_verify_shims_fallback() { offer_token: auction_accounts.offer_token, }; // Attempt to improve the offer using the non-fallback method + println!("Improving offer"); let _improved_offer_fixture = improve_offer(&testing_context.test_context, auction_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; + println!("Offer improved"); // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } @@ -302,5 +338,6 @@ pub async fn test_execute_order_fallback() { &execute_order_fallback_accounts, ).await.expect("Failed to execute order"); + // Figure out why the solver balance is not increased here println!("Solver balance after executing order: {:?}", solver.get_balance(&testing_context.test_context).await); } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index e4b753680..1d38d475b 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -33,6 +33,7 @@ impl PreTestingContext { ); program_test.set_compute_max_units(1000000000); program_test.set_transaction_account_lock_limit(1000); + // Setup Testing Actors let testing_actors = TestingActors::new(owner_keypair_path); @@ -119,12 +120,13 @@ impl Solver { pub async fn approve_usdc(&self, test_context: &Rc>, delegate: &Pubkey, amount: u64) { // If signer pubkeys are empty, it means that the owner is the signer - let approve_ix = approve(&spl_token::ID, &self.token_account_address().unwrap(), delegate, &self.actor.pubkey(), &[], amount).expect("Failed to approve USDC"); + let last_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + let approve_ix = approve(&spl_token::ID, &self.token_account_address().unwrap(), delegate, &self.actor.pubkey(), &[], amount).expect("Failed to create approve USDC instruction"); let transaction = Transaction::new_signed_with_payer( &[approve_ix], Some(&self.actor.pubkey()), &[&self.actor.keypair()], - test_context.borrow().last_blockhash, + last_blockhash, ); test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); } diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 3171bcc47..058b46162 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -27,6 +27,11 @@ use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, }; +use matching_engine::fallback::initialise_fast_market_order::{ + InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, + InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, + InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, +}; use wormhole_svm_definitions::borsh::GuardianSignatures; #[allow(dead_code)] @@ -170,12 +175,12 @@ fn set_up_post_message_transaction( } pub async fn add_guardian_signatures_account(test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32) -> Result { - let recent_blockhash = test_ctx.borrow().last_blockhash; + let new_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, recent_blockhash); + let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, new_blockhash); test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to add guardian signatures account"); - + Ok(signatures_signer.pubkey()) } @@ -352,84 +357,33 @@ pub struct PlaceInitialOfferShimFixture { /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. pub async fn place_initial_offer_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, auction_accounts: &super::auction::AuctionAccounts, offer_price: u64) -> Result { - - let vaa_data = super::vaa::PostedVaaData { - consistency_level: vaa_data.consistency_level, - vaa_time: vaa_data.vaa_time, - sequence: vaa_data.sequence, - emitter_chain: vaa_data.emitter_chain, - emitter_address: vaa_data.emitter_address, - payload: vaa_data.payload.clone(), - nonce: 0, - vaa_signature_account: vaa_data.vaa_signature_account, - submission_time: 0, - }; - let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( - vaa_data.consistency_level, - vaa_data.vaa_time, - vaa_data.sequence, - vaa_data.emitter_chain, - vaa_data.emitter_address, - ); - - let vaa_digest = vaa_data.digest(); + let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); - let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_digest], &program_id).0; + let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.digest], &program_id).0; let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; - let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); - - let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); - let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); - let signatures_signer = Rc::new(Keypair::new()); - let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, &signatures_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); + + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = create_guardian_signatures(test_ctx, payer_signer, &vaa_data, wormhole_program_id, Some(&solver.keypair())).await; + - let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()], program_id).0; // Approve the transfer authority let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; - + super::setup::fast_forward_slots(test_ctx, 5).await; + let solver_usdc_balance = solver.get_balance(test_ctx).await; + println!("Solver USDC balance: {:?}", solver_usdc_balance); solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; - let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); - - let redeemer_message_fixed_length = { - let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) - - if !order.redeemer_message.is_empty() { - // Calculate how many bytes to copy (min of message length and array size) - let copy_len = std::cmp::min(order.redeemer_message.len(), 512); - - // Copy the bytes from the message to the fixed array - fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); - } - - fixed_array - }; - let fast_market_order = FastMarketOrderState { - amount_in: order.amount_in, - min_amount_out: order.min_amount_out, - deadline: order.deadline, - target_chain: order.target_chain, - redeemer_message_length: order.redeemer_message.len() as u16, - redeemer: order.redeemer, - sender: order.sender, - refund_address: order.refund_address, - max_fee: order.max_fee, - init_auction_fee: order.init_auction_fee, - redeemer_message: redeemer_message_fixed_length, - }; + // Create the fast market order account + let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], program_id).0; - assert_eq!(fast_market_order.redeemer, order.redeemer); - assert_eq!(vaa_message.digest(&fast_market_order).as_ref(), vaa_data.digest().as_ref()); + let create_fast_market_order_ix = initialise_fast_market_order_fallback_instruction(payer_signer, program_id, fast_market_order, guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump); - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price, vaa_data.sequence, vaa_data.vaa_time, guardian_set_bump, vaa_data.consistency_level, fast_market_order); + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price, vaa_data.sequence, vaa_data.vaa_time, vaa_data.consistency_level); let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { signer: &payer_signer.pubkey(), transfer_authority: &transfer_authority, custodian: &auction_accounts.custodian, auction_config: &auction_accounts.auction_config, - guardian_set: &guardian_set_pubkey, - guardian_set_signatures: &guardian_signatures_pubkey, from_endpoint: &auction_accounts.from_router_endpoint, to_endpoint: &auction_accounts.to_router_endpoint, fast_market_order: &fast_market_order_account, @@ -437,7 +391,6 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, guardian_set_pubkey: Pubkey, guardian_signatures_pubkey: Pubkey, guardian_set_bump: u8) -> solana_program::instruction::Instruction { + let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], program_id).0; + + let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { + signer: &payer_signer.pubkey(), + fast_market_order_account: &fast_market_order_account, + guardian_set: &guardian_set_pubkey, + guardian_set_signatures: &guardian_signatures_pubkey, + verify_vaa_shim_program: &WORMHOLE_VERIFY_VAA_SHIM_PID, + system_program: &solana_program::system_program::ID, + }; + + InitialiseFastMarketOrderFallback { + program_id: program_id, + accounts: create_fast_market_order_accounts, + data: InitialiseFastMarketOrderFallbackData::new(fast_market_order, guardian_set_bump), + }.instruction() +} + +pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::PostedVaaData, refund_recipient: Pubkey) -> (FastMarketOrderState, super::vaa::PostedVaaData) { + let vaa_data = super::vaa::PostedVaaData { + consistency_level: vaa_data.consistency_level, + vaa_time: vaa_data.vaa_time, + sequence: vaa_data.sequence, + emitter_chain: vaa_data.emitter_chain, + emitter_address: vaa_data.emitter_address, + payload: vaa_data.payload.clone(), + nonce: 0, + vaa_signature_account: vaa_data.vaa_signature_account, + submission_time: 0, + }; + let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( + vaa_data.consistency_level, + vaa_data.vaa_time, + vaa_data.sequence, + vaa_data.emitter_chain, + vaa_data.emitter_address, + ); + + let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); + + let redeemer_message_fixed_length = { + let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) + + if !order.redeemer_message.is_empty() { + // Calculate how many bytes to copy (min of message length and array size) + let copy_len = std::cmp::min(order.redeemer_message.len(), 512); + + // Copy the bytes from the message to the fixed array + fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); + } + + fixed_array + }; + let fast_market_order = FastMarketOrderState::new( + order.amount_in, + order.min_amount_out, + order.deadline, + order.target_chain, + order.redeemer_message.len() as u16, + order.redeemer, + order.sender, + order.refund_address, + order.max_fee, + order.init_auction_fee, + redeemer_message_fixed_length, + vaa_data.digest(), + refund_recipient.to_bytes(), + vaa_data.sequence, + vaa_data.vaa_time, + vaa_data.emitter_chain, + vaa_data.emitter_address, + ); + + assert_eq!(fast_market_order.redeemer, order.redeemer); + assert_eq!(vaa_message.digest(&fast_market_order).as_ref(), vaa_data.digest().as_ref()); + + (fast_market_order, vaa_data) +} + +pub async fn create_guardian_signatures(test_ctx: &Rc>, payer_signer: &Rc, vaa_data: &super::vaa::PostedVaaData, wormhole_program_id: &Pubkey, guardian_signature_signer: Option<&Rc>) -> (Pubkey, Pubkey, u8) { + let new_keypair = Rc::new(Keypair::new()); + let guardian_signature_signer = guardian_signature_signer.unwrap_or(&new_keypair); + let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); + let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); + let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, guardian_signature_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); + (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) +} diff --git a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs index 8b97ace68..28580c343 100644 --- a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs @@ -47,7 +47,13 @@ impl ExecuteOrderFallbackAccounts { } } -pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts) -> Result<()> { +pub struct ExecuteOrderFallbackFixture { + pub cctp_message: Pubkey, + pub post_message_sequence: Pubkey, + pub post_message_message: Pubkey, +} + +pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts) -> Result { // Get target chain and use as remote address let cctp_message = Pubkey::find_program_address(&[common::CCTP_MESSAGE_SEED_PREFIX, &execute_order_fallback_accounts.active_auction.to_bytes()], program_id).0; @@ -105,10 +111,14 @@ pub async fn execute_order_fallback(test_ctx: &Rc>, // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; - super::setup::fast_forward_slots(test_ctx, 20).await; - println!("Fast forwarded 20 slots"); + super::setup::fast_forward_slots(test_ctx, 1).await; + println!("Fast forwarded 1 slots"); let transaction = Transaction::new_signed_with_payer(&[execute_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to execute order"); - Ok(()) + Ok(ExecuteOrderFallbackFixture { + cctp_message, + post_message_sequence, + post_message_message, + }) } \ No newline at end of file From 069b689be1a490894f7fb3eafdd3969e99608ab0 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 11 Mar 2025 15:36:11 +0000 Subject: [PATCH 016/112] [broken] smaller test to reproduce deadlineExceeded --- .../tests/initialize_integration_tests.rs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 9c833d666..dfd799086 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,3 +1,5 @@ +use anchor_lang::AccountDeserialize; +use anchor_spl::token::TokenAccount; use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -176,7 +178,7 @@ pub async fn test_initialise_fast_market_order_fallback() { let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; - + let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( &testing_context.testing_actors.owner.keypair(), &PROGRAM_ID, @@ -190,6 +192,38 @@ pub async fn test_initialise_fast_market_order_fallback() { testing_context.test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to initialise fast market order"); } +#[tokio::test] +pub async fn test_approve_usdc() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. + let vaa_data = { + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); + let first_test_ft = vaas_test.0.first().unwrap(); + first_test_ft.fast_transfer_vaa.clone().vaa_data + }; + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + + let actors = testing_context.testing_actors; + let solver = actors.solvers[0].clone(); + let offer_price: u64 = 1__000_000; + let program_id = PROGRAM_ID; + let new_pubkey = Pubkey::new_unique(); + + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; + + let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &new_pubkey.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; + solver.approve_usdc(&testing_context.test_context, &transfer_authority, offer_price).await; + let usdc_balance = solver.get_balance(&testing_context.test_context).await; + + println!("Solver USDC balance: {:?}", usdc_balance); + let solver_token_account_address = solver.token_account_address().unwrap(); + let solver_token_account_info = testing_context.test_context.borrow_mut().banks_client.get_account(solver_token_account_address).await.expect("Failed to query banks client for solver token account info").expect("Failed to get solver token account info"); + let solver_token_account = TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); + assert!(solver_token_account.delegate.is_some()); +} + #[tokio::test] // Testing a initial offer from arbitrum to ethereum // TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction From 846120cde68d91de8e87992e2c5260d07697a1be Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 11 Mar 2025 17:46:44 +0000 Subject: [PATCH 017/112] made sure post signatures program is being added, still broken --- .../tests/initialize_integration_tests.rs | 15 +++++++++------ .../matching-engine/tests/utils/airdrop.rs | 9 ++++++--- .../matching-engine/tests/utils/initialize.rs | 8 +++++--- .../matching-engine/tests/utils/router.rs | 13 +++++++++---- .../programs/matching-engine/tests/utils/setup.rs | 5 +++-- .../programs/matching-engine/tests/utils/shims.rs | 4 ++-- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index dfd799086..eeeb85ec9 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -5,6 +5,7 @@ use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; mod utils; use solana_sdk::signer::Signer; +use solana_sdk::transaction::VersionedTransaction; use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; @@ -189,12 +190,15 @@ pub async fn test_initialise_fast_market_order_fallback() { ); let recent_blockhash = testing_context.test_context.borrow().last_blockhash; let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer(&[initialise_fast_market_order_ix], Some(&testing_context.testing_actors.owner.pubkey()), &[&testing_context.testing_actors.owner.keypair()], recent_blockhash); - testing_context.test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to initialise fast market order"); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + testing_context.test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to initialise fast market order"); } #[tokio::test] pub async fn test_approve_usdc() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + pre_testing_context.add_post_message_shims(); let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. @@ -210,7 +214,9 @@ pub async fn test_approve_usdc() { let offer_price: u64 = 1__000_000; let program_id = PROGRAM_ID; let new_pubkey = Pubkey::new_unique(); - + + // Warp to a new slot + utils::setup::fast_forward_slots(&testing_context.test_context, 1).await; let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &new_pubkey.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; @@ -230,6 +236,7 @@ pub async fn test_approve_usdc() { pub async fn test_place_initial_offer_fallback() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); pre_testing_context.add_verify_shims(); + pre_testing_context.add_post_message_shims(); // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); @@ -245,7 +252,6 @@ pub async fn test_place_initial_offer_fallback() { // Try making initial offer using the shim instruction let usdc_mint_address = USDC_MINT_ADDRESS; let auction_config_address = initialize_fixture.get_auction_config_address(); - println!("Creating router endpoints"); let router_endpoints = create_all_router_endpoints_test( &testing_context.test_context, testing_context.testing_actors.owner.pubkey(), @@ -256,7 +262,6 @@ pub async fn test_place_initial_offer_fallback() { testing_context.testing_actors.owner.keypair(), PROGRAM_ID, ).await; - println!("Router endpoints created"); let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; @@ -274,7 +279,6 @@ pub async fn test_place_initial_offer_fallback() { let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; // Place initial offer using the fallback program - println!("Placing initial offer"); let initial_offer_fixture = place_initial_offer_fallback( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), @@ -285,7 +289,6 @@ pub async fn test_place_initial_offer_fallback() { &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) ).await.expect("Failed to place initial offer"); - println!("Initial offer placed"); let auction_offer_fixture = AuctionOfferFixture { auction_address: initial_offer_fixture.auction_address, auction_custody_token_address: initial_offer_fixture.auction_custody_token_address, diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index 53d10924a..d4aef5a12 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -7,7 +7,7 @@ use solana_sdk::{ system_instruction, signature::Signer, }; -use solana_sdk::transaction::Transaction; +use solana_sdk::transaction::{Transaction, VersionedTransaction}; use super::constants; @@ -49,6 +49,7 @@ pub async fn airdrop_usdc( recipient_ata: &Pubkey, amount: u64, ) { + let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); let usdc_mint_address = constants::USDC_MINT; let mint_to_ix = spl_token::instruction::mint_to( &spl_token::ID, @@ -62,8 +63,10 @@ pub async fn airdrop_usdc( &[mint_to_ix.clone()], Some(&test_context.borrow().payer.pubkey()), &[&test_context.borrow().payer], - test_context.borrow().last_blockhash, + new_blockhash, ); - test_context.borrow_mut().banks_client.process_transaction(tx).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(tx).expect("Failed to convert transaction to versioned transaction"); + + test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); } diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/utils/initialize.rs index 24f720210..2fac922a0 100644 --- a/solana/programs/matching-engine/tests/utils/initialize.rs +++ b/solana/programs/matching-engine/tests/utils/initialize.rs @@ -1,6 +1,6 @@ use solana_program_test::ProgramTestContext; use solana_sdk::{ - instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction + instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::{Transaction, VersionedTransaction} }; use std::rc::Rc; use std::cell::RefCell; @@ -152,10 +152,12 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu &[instruction], Some(&test_context.borrow().payer.pubkey()), ); - transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], test_context.borrow().last_blockhash); + let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], new_blockhash); // Process transaction - test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); // Verify the results let custodian_account = test_context.borrow_mut().banks_client diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 975258df7..f9b0266c6 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -8,6 +8,7 @@ use matching_engine::state::Custodian; use matching_engine::state::EndpointInfo; use matching_engine::LOCAL_CUSTODY_TOKEN_SEED_PREFIX; use solana_program_test::ProgramTestContext; +use solana_sdk::transaction::VersionedTransaction; use std::rc::Rc; use std::cell::RefCell; use matching_engine::instruction::{AddCctpRouterEndpoint, AddLocalRouterEndpoint}; @@ -189,9 +190,11 @@ pub async fn add_cctp_router_endpoint_ix( Some(&test_context.borrow().payer.pubkey()), ); // TODO: Figure out who the signers are - transaction.sign(&[&test_context.borrow().payer, &admin_keypair], test_context.borrow().last_blockhash); + let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + transaction.sign(&[&test_context.borrow().payer, &admin_keypair], new_blockhash); - test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); let endpoint_account = test_context.borrow_mut().banks_client .get_account(router_endpoint_address) @@ -250,9 +253,11 @@ pub async fn add_local_router_endpoint_ix( &[instruction], Some(&test_context.borrow().payer.pubkey()), ); - transaction.sign(&[&test_context.borrow().payer, &admin_keypair], test_context.borrow().last_blockhash); + let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + transaction.sign(&[&test_context.borrow().payer, &admin_keypair], new_blockhash); - test_context.borrow_mut().banks_client.process_transaction(transaction).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); let endpoint_account = test_context.borrow_mut().banks_client .get_account(router_endpoint_address) diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 1d38d475b..39cb3cbe5 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -1,7 +1,7 @@ use anchor_lang::AccountDeserialize; use solana_program_test::{ProgramTest, ProgramTestContext}; use solana_sdk::{ - pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::{Transaction, VersionedTransaction} }; use std::rc::Rc; use std::cell::RefCell; @@ -128,7 +128,8 @@ impl Solver { &[&self.actor.keypair()], last_blockhash, ); - test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to approve USDC"); } pub async fn get_balance(&self, test_context: &Rc>) -> u64 { diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 058b46162..e99d536b7 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -176,9 +176,9 @@ fn set_up_post_message_transaction( pub async fn add_guardian_signatures_account(test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32) -> Result { let new_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - + let current_blockhash = test_ctx.borrow().last_blockhash; + assert_eq!(new_blockhash, current_blockhash); let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, new_blockhash); - test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to add guardian signatures account"); Ok(signatures_signer.pubkey()) From 0cf39745677e264e07da406283ac1f50112a3e81 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 11 Mar 2025 18:37:52 +0000 Subject: [PATCH 018/112] [maybe working?] changing keypairs for signers? --- .../tests/initialize_integration_tests.rs | 14 ++++++++------ .../programs/matching-engine/tests/utils/setup.rs | 6 +++--- .../programs/matching-engine/tests/utils/shims.rs | 13 ++++++------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index eeeb85ec9..fa5f19e6a 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -214,13 +214,14 @@ pub async fn test_approve_usdc() { let offer_price: u64 = 1__000_000; let program_id = PROGRAM_ID; let new_pubkey = Pubkey::new_unique(); - - // Warp to a new slot - utils::setup::fast_forward_slots(&testing_context.test_context, 1).await; - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; + // TODO: Figure out why if this is placed before the approve_usdc call, the test fails ... + let second_solver = actors.solvers[1].clone(); + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&second_solver.keypair())).await; + let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &new_pubkey.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; solver.approve_usdc(&testing_context.test_context, &transfer_authority, offer_price).await; + let usdc_balance = solver.get_balance(&testing_context.test_context).await; println!("Solver USDC balance: {:?}", usdc_balance); @@ -295,9 +296,10 @@ pub async fn test_place_initial_offer_fallback() { offer_price: 1__000_000, offer_token: auction_accounts.offer_token, }; - // Attempt to improve the offer using the non-fallback method + // Attempt to improve the offer using the non-fallback method with another solver making the improved offer println!("Improving offer"); - let _improved_offer_fixture = improve_offer(&testing_context.test_context, auction_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; + let second_solver = testing_context.testing_actors.solvers[1].clone(); + let _improved_offer_fixture = improve_offer(&testing_context.test_context, auction_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, second_solver, auction_config_address).await; println!("Offer improved"); // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 39cb3cbe5..5045756dc 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -1,7 +1,7 @@ use anchor_lang::AccountDeserialize; use solana_program_test::{ProgramTest, ProgramTestContext}; use solana_sdk::{ - pubkey::Pubkey, signature::{Keypair, Signer}, transaction::{Transaction, VersionedTransaction} + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, }; use std::rc::Rc; use std::cell::RefCell; @@ -128,8 +128,8 @@ impl Solver { &[&self.actor.keypair()], last_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to approve USDC"); + + test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); } pub async fn get_balance(&self, test_context: &Rc>) -> u64 { diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index e99d536b7..40fcfa0a1 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -176,8 +176,6 @@ fn set_up_post_message_transaction( pub async fn add_guardian_signatures_account(test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32) -> Result { let new_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - let current_blockhash = test_ctx.borrow().last_blockhash; - assert_eq!(new_blockhash, current_blockhash); let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, new_blockhash); test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to add guardian signatures account"); @@ -362,16 +360,17 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc Date: Tue, 11 Mar 2025 18:44:23 +0000 Subject: [PATCH 019/112] [weird but working] --- .../matching-engine/tests/initialize_integration_tests.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index fa5f19e6a..b4b585c6b 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -214,15 +214,14 @@ pub async fn test_approve_usdc() { let offer_price: u64 = 1__000_000; let program_id = PROGRAM_ID; let new_pubkey = Pubkey::new_unique(); - - // TODO: Figure out why if this is placed before the approve_usdc call, the test fails ... - let second_solver = actors.solvers[1].clone(); - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&second_solver.keypair())).await; let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &new_pubkey.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; solver.approve_usdc(&testing_context.test_context, &transfer_authority, offer_price).await; let usdc_balance = solver.get_balance(&testing_context.test_context).await; + + // TODO: Figure out why if this is placed before the approve_usdc call, the test fails ... + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); From b4aa6b93c17cbccb725408508fa866e434de75eb Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 14 Mar 2025 19:13:17 +0000 Subject: [PATCH 020/112] doing cctp message magic --- solana/programs/matching-engine/Cargo.toml | 1 + .../processor/close_fast_market_order.rs | 8 +- .../src/fallback/processor/errors.rs | 3 +- .../src/fallback/processor/execute_order.rs | 4 +- .../processor/prepare_order_response.rs | 229 +++++-- .../fallback/processor/process_instruction.rs | 3 + .../tests/initialize_integration_tests.rs | 128 +++- .../tests/utils/cctp_message.rs | 607 ++++++++++++++++++ .../matching-engine/tests/utils/constants.rs | 1 - .../matching-engine/tests/utils/initialize.rs | 2 + .../matching-engine/tests/utils/mod.rs | 2 + .../matching-engine/tests/utils/shims.rs | 26 +- .../tests/utils/shims_execute_order.rs | 16 + .../utils/shims_prepare_order_response.rs | 251 ++++++++ .../matching-engine/tests/utils/vaa.rs | 22 +- 15 files changed, 1243 insertions(+), 60 deletions(-) create mode 100644 solana/programs/matching-engine/tests/utils/cctp_message.rs create mode 100644 solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 05834e68d..3bde7f295 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -56,6 +56,7 @@ lazy_static = "1.4.0" bs58 = "0.5.0" serde = { version = "1.0.212", features = ["derive"] } secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context", "recovery"] } +num-traits = "0.2.16" wormhole-svm-shim.workspace = true wormhole-svm-definitions.workspace = true diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 3e3e87ec9..1feafddb7 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -3,6 +3,8 @@ use solana_program::program_error::ProgramError; use solana_program::instruction::Instruction; use crate::state::FastMarketOrder; +use super::FallbackMatchingEngineInstruction; + pub struct CloseFastMarketOrderAccounts<'ix> { pub fast_market_order: &'ix Pubkey, pub refund_recipient: &'ix Pubkey, @@ -11,8 +13,8 @@ pub struct CloseFastMarketOrderAccounts<'ix> { impl<'ix> CloseFastMarketOrderAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ - AccountMeta::new_readonly(*self.fast_market_order, false), - AccountMeta::new_readonly(*self.refund_recipient, false), + AccountMeta::new(*self.fast_market_order, false), + AccountMeta::new(*self.refund_recipient, false), ] } } @@ -27,7 +29,7 @@ impl CloseFastMarketOrder<'_> { Instruction { program_id: *self.program_id, accounts: self.accounts.to_account_metas(), - data: vec![], + data: FallbackMatchingEngineInstruction::CloseFastMarketOrder.to_vec(), } } } diff --git a/solana/programs/matching-engine/src/fallback/processor/errors.rs b/solana/programs/matching-engine/src/fallback/processor/errors.rs index 3c813afd5..dfc9f8f05 100644 --- a/solana/programs/matching-engine/src/fallback/processor/errors.rs +++ b/solana/programs/matching-engine/src/fallback/processor/errors.rs @@ -30,5 +30,6 @@ pub enum FallbackError { #[msg("Invalid CCTP message")] InvalidCctpMessage, - // Add more error variants as needed + #[msg("Invalid program")] + InvalidProgram, } \ No newline at end of file diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 797421142..de0780ca2 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -178,6 +178,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Do checks // ------------------------------------------------------------------------------------------------ + let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; // Bind value for compiler (needed for pda seeds) let active_auction_key = active_auction_account.key(); @@ -219,7 +220,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Check fast market order seeds let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, - active_auction_key.as_ref(), + fast_market_order_zero_copy.digest.as_ref(), + fast_market_order_zero_copy.refund_recipient.as_ref(), ]; let (fast_market_order_pda, _fast_market_order_bump) = Pubkey::find_program_address(&fast_market_order_seeds, program_id); diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index e051a5320..6c5a86b3c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -2,6 +2,7 @@ use std::io::Cursor; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; +use solana_program::program_pack::Pack; use common::messages::raw::SlowOrderResponse; use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; use common::wormhole_cctp_solana::utils::CctpMessage; @@ -21,12 +22,37 @@ use crate::error::MatchingEngineError; pub struct PrepareOrderResponseCctpShimData { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, - pub deposit_payload: Vec, + pub finalized_vaa_message: FinalizedVaaMessage, +} + +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct FinalizedVaaMessage { + pub vaa_sequence: u64, + pub vaa_timestamp: u32, + pub vaa_emitter_chain: u16, + pub vaa_emitter_address: [u8; 32], + pub base_fee: u64, + pub deposit_message: DepositMessage, + pub guardian_set_bump: u8, +} + +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct DepositMessage { + pub token_address: [u8; 32], + pub amount: [u8; 32], // little endian + pub source_cctp_domain: u32, + pub destination_cctp_domain: u32, + pub cctp_nonce: u64, + pub burn_source: [u8; 32], + pub mint_recipient: [u8; 32], + pub digest: [u8; 32], + pub payload_len: u16, + pub payload: Vec, } impl PrepareOrderResponseCctpShimData { - pub fn new(encoded_cctp_message: Vec, cctp_attestation: Vec, deposit_payload: Vec) -> Self { - Self { encoded_cctp_message, cctp_attestation, deposit_payload } + pub fn new(encoded_cctp_message: Vec, cctp_attestation: Vec, finalized_vaa_message: FinalizedVaaMessage) -> Self { + Self { encoded_cctp_message, cctp_attestation, finalized_vaa_message } } pub fn from_bytes(data: &[u8]) -> Option { Self::try_from_slice(data).ok() @@ -68,15 +94,14 @@ pub struct PrepareOrderResponseCctpShimAccounts<'ix> { pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, pub cctp_token_messenger_minter_program: &'ix Pubkey, pub cctp_message_transmitter_program: &'ix Pubkey, + pub guardian_set: &'ix Pubkey, + pub guardian_set_signatures: &'ix Pubkey, + pub verify_shim_program: &'ix Pubkey, pub token_program: &'ix Pubkey, pub system_program: &'ix Pubkey, } impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { - pub fn new(signer: &'ix Pubkey, custodian: &'ix Pubkey, fast_market_order: &'ix Pubkey, from_endpoint: &'ix Pubkey, to_endpoint: &'ix Pubkey, prepared_order_response: &'ix Pubkey, prepared_custody_token: &'ix Pubkey, base_fee_token: &'ix Pubkey, usdc: &'ix Pubkey, cctp_mint_recipient: &'ix Pubkey, cctp_message_transmitter_authority: &'ix Pubkey, cctp_message_transmitter_config: &'ix Pubkey, cctp_used_nonces: &'ix Pubkey, cctp_message_transmitter_event_authority: &'ix Pubkey, cctp_token_messenger: &'ix Pubkey, cctp_remote_token_messenger: &'ix Pubkey, cctp_token_minter: &'ix Pubkey, cctp_local_token: &'ix Pubkey, cctp_token_pair: &'ix Pubkey, cctp_token_messenger_minter_custody_token: &'ix Pubkey, cctp_token_messenger_minter_event_authority: &'ix Pubkey, cctp_token_messenger_minter_program: &'ix Pubkey, cctp_message_transmitter_program: &'ix Pubkey, token_program: &'ix Pubkey, system_program: &'ix Pubkey) -> Self { - Self { signer, custodian, fast_market_order, from_endpoint, to_endpoint, prepared_order_response, prepared_custody_token, base_fee_token, usdc, cctp_mint_recipient, cctp_local_token, cctp_message_transmitter_authority, cctp_message_transmitter_config, cctp_message_transmitter_event_authority, cctp_message_transmitter_program, cctp_remote_token_messenger, cctp_token_messenger, cctp_token_messenger_minter_custody_token, cctp_token_messenger_minter_event_authority, cctp_token_messenger_minter_program, cctp_token_minter, cctp_token_pair, cctp_used_nonces, token_program, system_program } - } - pub fn to_account_metas(&self) -> Vec { vec![ AccountMeta::new(*self.signer, false), @@ -102,6 +127,9 @@ impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { AccountMeta::new_readonly(*self.cctp_token_messenger_minter_event_authority, false), AccountMeta::new_readonly(*self.cctp_token_messenger_minter_program, false), AccountMeta::new_readonly(*self.cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*self.guardian_set, false), + AccountMeta::new_readonly(*self.guardian_set_signatures, false), + AccountMeta::new_readonly(*self.verify_shim_program, false), AccountMeta::new_readonly(*self.token_program, false), AccountMeta::new_readonly(*self.system_program, false), ] @@ -126,7 +154,7 @@ impl<'ix> PrepareOrderResponseCctpShim<'ix> { pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData) -> Result<()> { let program_id = &crate::ID; - if accounts.len() < 24 { + if accounts.len() < 27 { return Err(ErrorCode::AccountNotEnoughKeys.into()); } let signer = &accounts[0]; @@ -152,8 +180,15 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO let cctp_token_messenger_minter_event_authority = &accounts[20]; let cctp_token_messenger_minter_program = &accounts[21]; let cctp_message_transmitter_program = &accounts[22]; - let token_program = &accounts[23]; - let system_program = &accounts[24]; + let guardian_set = &accounts[23]; + let guardian_set_signatures = &accounts[24]; + let _verify_shim_program = &accounts[25]; + let token_program = &accounts[26]; + let system_program = &accounts[27]; + + let receive_message_args = data.to_receive_message_args(); + let finalized_vaa_message = data.finalized_vaa_message; + let deposit_message = finalized_vaa_message.deposit_message; // Check that fast market order is owned by the program if fast_market_order.owner != program_id { @@ -166,12 +201,34 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; // Load from cctp message. let cctp_message = CctpMessage::parse(&data.encoded_cctp_message).map_err(|_| FallbackError::InvalidCctpMessage)?; + let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; // Deserialise the to_endpoint account let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; // Deserialise the from_endpoint account let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; + let guardian_set_bump = finalized_vaa_message.guardian_set_bump; + + // Check loaded vaa is deposit message + let slow_order_response = SlowOrderResponse::parse(&deposit_message.payload).map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; + + // Create pdas for addresses that need to be created + // Check the prepared order response account is valid + let prepared_order_response_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_zero_copy.digest + ]; + + let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, program_id); + + let prepared_custody_token_seeds = [ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_pda.as_ref(), + ]; + + let (prepared_custody_token_pda, prepared_custody_token_bump) = Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); + // Check custodian account if custodian.owner != program_id { msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian.owner); @@ -229,23 +286,20 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO return Err(MatchingEngineError::InvalidTargetRouter.into()); } - // Check the prepared order response account is valid - let prepared_order_response_seeds = [ - PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_zero_copy.digest - ]; - - let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, program_id); - if prepared_order_response_pda != prepared_order_response.key() { msg!("Prepared order response pda is invalid"); return Err(FallbackError::InvalidPda.into()) - .map_err(|e: Error| e.with_account_name("prepared_order_response")); + .map_err(|e: Error| e.with_pubkeys((prepared_order_response_pda, prepared_order_response.key()))); + } + + if prepared_custody_token_pda != prepared_custody_token.key() { + msg!("Prepared custody token pda is invalid"); + return Err(FallbackError::InvalidPda.into()) + .map_err(|e: Error| e.with_pubkeys((prepared_custody_token_pda, prepared_custody_token.key()))); } - // TODO: Figure out how to convert source domain to emitter chain // Check vaa emitter chain matches fast market order emitter chain - if fast_market_order_zero_copy.vaa_emitter_chain != cctp_message.source_domain() as u16 { + if fast_market_order_zero_copy.vaa_emitter_chain != finalized_vaa_message.vaa_emitter_chain { msg!("Vaa emitter chain does not match fast market order emitter chain"); return Err(MatchingEngineError::VaaMismatch.into()) .map_err(|e: Error| e.with_account_name("fast_market_order")); @@ -253,28 +307,50 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO // TODO: Figure out how to find emitter address to check against // Check vaa emitter address matches fast market order emitter address - if fast_market_order_zero_copy.sender != cctp_message.sender() { + if fast_market_order_zero_copy.vaa_emitter_address != finalized_vaa_message.vaa_emitter_address { msg!("Vaa emitter address does not match fast market order emitter address"); return Err(MatchingEngineError::VaaMismatch.into()) .map_err(|e: Error| e.with_account_name("fast_market_order")); } // TODO: Figure out how to check the sequence number - // if fast_market_order_zero_copy.vaa_sequence != cctp_message.sequence().saturating_add(1) { - // msg!("Vaa sequence must be exactly 1 greater than fast market order sequence"); - // return Err(MatchingEngineError::VaaMismatch.into()) - // .map_err(|e: Error| e.with_account_name("fast_market_order")); - // } + if fast_market_order_zero_copy.vaa_sequence != finalized_vaa_message.vaa_sequence.saturating_add(1) { + msg!("Vaa sequence must be exactly 1 greater than fast market order sequence"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } // TODO: Figure out how to check the timestamp - // if fast_market_order_zero_copy.vaa_timestamp != cctp_message.timestamp() { - // msg!("Vaa timestamp does not match fast market order timestamp"); - // return Err(MatchingEngineError::VaaMismatch.into()) - // .map_err(|e: Error| e.with_account_name("fast_market_order")); - // } + if fast_market_order_zero_copy.vaa_timestamp != finalized_vaa_message.vaa_timestamp { + msg!("Vaa timestamp does not match fast market order timestamp"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } - // Check loaded vaa is deposit message - let slow_order_response = SlowOrderResponse::parse(&data.deposit_payload).map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; + // TODO: Make checks against cctp message + if cctp_message.sender() != fast_market_order_zero_copy.sender { + msg!("Cctp message sender does not match fast market order sender"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + + if cctp_message.destination_domain() != deposit_message.destination_cctp_domain { + msg!("Cctp message destination domain does not match deposit message destination domain"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + + if cctp_message.nonce() != deposit_message.cctp_nonce { + msg!("Cctp message nonce does not match deposit message nonce"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } + + if cctp_message.source_domain() != deposit_message.source_cctp_domain { + msg!("Cctp message source domain does not match deposit message source domain"); + return Err(MatchingEngineError::VaaMismatch.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order")); + } // Check the base token fee key is not equal to the prepared custody token key if base_fee_token.key() == prepared_custody_token.key() { @@ -282,7 +358,52 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO return Err(MatchingEngineError::InvalidBaseFeeToken.into()) .map_err(|e: Error| e.with_account_name("base_fee_token")); } - + + if token_program.key() != spl_token::ID { + msg!("Token program is invalid"); + return Err(FallbackError::InvalidProgram.into()) + .map_err(|e: Error| e.with_account_name("token_program")); + } + + if _verify_shim_program.key() != wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID { + msg!("Verify shim program is invalid"); + return Err(FallbackError::InvalidProgram.into()) + .map_err(|e: Error| e.with_account_name("verify_shim_program")); + } + + if system_program.key() != solana_program::system_program::ID { + msg!("System program is invalid"); + return Err(FallbackError::InvalidProgram.into()) + .map_err(|e: Error| e.with_account_name("system_program")); + } + + // Verify deposit message shim using verify shim program + + // Start verify deposit message vaa shim + // ------------------------------------------------------------------------------------------------ + let verify_hash_data = { + let mut data = vec![]; + data.extend_from_slice(&wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR); + data.push(guardian_set_bump); + data.extend_from_slice(&fast_market_order_zero_copy.digest); + data + }; + + let verify_shim_ix = Instruction { + program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: vec![ + AccountMeta::new_readonly(guardian_set.key(), false), + AccountMeta::new_readonly(guardian_set_signatures.key(), false), + ], + data: verify_hash_data, + }; + invoke_signed_unchecked(&verify_shim_ix, &[ + guardian_set.to_account_info(), + guardian_set_signatures.to_account_info(), + ], &[])?; + // End verify deposit message vaa shim + // ------------------------------------------------------------------------------------------------ + // Start create prepared order response account // ------------------------------------------------------------------------------------------------ @@ -336,7 +457,41 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO // End create prepared order response account // ------------------------------------------------------------------------------------------------ - + + // Start create prepared custody token account + // ------------------------------------------------------------------------------------------------ + let create_prepared_custody_token_seeds = [ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_pda.as_ref(), + &[prepared_custody_token_bump], + ]; + + let prepared_custody_token_signer_seeds = &[&create_prepared_custody_token_seeds[..]]; + let prepared_custody_token_account_space = spl_token::state::Account::LEN; + create_account_reliably( + &signer.key(), + &prepared_custody_token_pda, + prepared_custody_token.lamports(), + prepared_custody_token_account_space, + accounts, + program_id, + prepared_custody_token_signer_seeds, + )?; + let init_token_account_ix = spl_token::instruction::initialize_account3( + &spl_token::ID, + &prepared_custody_token_pda, + &usdc.key(), + &prepared_custody_token.key(), + ).unwrap(); + + solana_program::program::invoke( + &init_token_account_ix, + accounts, + ).unwrap(); + + // End create prepared custody token account + // ------------------------------------------------------------------------------------------------ + // Create cpi context for verify_vaa_and_mint message_transmitter_program::cpi::receive_token_messenger_minter_message( CpiContext::new_with_signer( @@ -363,7 +518,7 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO }, &[Custodian::SIGNER_SEEDS], ), - data.to_receive_message_args(), + receive_message_args, )?; // Finally transfer minted via CCTP to prepared custody token. diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 8cc470266..0a9761da2 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -69,6 +69,9 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..]))) }, + FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { + Some(Self::CloseFastMarketOrder) + }, _ => None, } } diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index b4b585c6b..7d58f6338 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,5 +1,7 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; +use common::wormhole_cctp_solana::cctp::token_messenger_minter_program::RemoteTokenMessenger; +use matching_engine::state::FastMarketOrder; use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -7,6 +9,7 @@ mod utils; use solana_sdk::signer::Signer; use solana_sdk::transaction::VersionedTransaction; use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; +use utils::shims_prepare_order_response::{PrepareOrderResponseShimAccountsFixture, PrepareOrderResponseShimDataFixture}; use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; use utils::initialize::initialize_program; @@ -178,7 +181,7 @@ pub async fn test_initialise_fast_market_order_fallback() { let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( &testing_context.testing_actors.owner.keypair(), @@ -194,6 +197,43 @@ pub async fn test_initialise_fast_market_order_fallback() { testing_context.test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to initialise fast market order"); } +#[tokio::test] +pub async fn test_close_fast_market_order_fallback() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + + // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let first_test_ft = vaas_test.0.first().unwrap(); + let solver = testing_context.testing_actors.solvers[0].clone(); + + let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; + + let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + &testing_context.testing_actors.owner.keypair(), + &PROGRAM_ID, + fast_market_order, + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ); + let recent_blockhash = testing_context.test_context.borrow().last_blockhash; + // Get balance of solver before initialising fast market order + let solver_balance_before = testing_context.test_context.borrow_mut().banks_client.get_balance(solver.pubkey()).await.expect("Failed to get balance of solver"); + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer(&[initialise_fast_market_order_ix], Some(&testing_context.testing_actors.owner.pubkey()), &[&testing_context.testing_actors.owner.keypair()], recent_blockhash); + let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); + testing_context.test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to initialise fast market order"); + let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrder::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], &PROGRAM_ID).0; + utils::shims::close_fast_market_order_fallback(&testing_context.test_context, &solver.keypair(), &PROGRAM_ID, &fast_market_order_account).await; + let solver_balance_after = testing_context.test_context.borrow_mut().banks_client.get_balance(solver.pubkey()).await.expect("Failed to get balance of solver"); + assert!(solver_balance_after > solver_balance_before, "Solver balance before initialising fast market order was {:?}, but after closing it was {:?}, though it should have been greater", solver_balance_before, solver_balance_after); +} + #[tokio::test] pub async fn test_approve_usdc() { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); @@ -220,8 +260,8 @@ pub async fn test_approve_usdc() { let usdc_balance = solver.get_balance(&testing_context.test_context).await; - // TODO: Figure out why if this is placed before the approve_usdc call, the test fails ... - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, Some(&solver.keypair())).await; + // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); @@ -303,7 +343,6 @@ pub async fn test_place_initial_offer_fallback() { // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } - #[tokio::test] // Testing an execute order from arbitrum to ethereum // TODO: Flesh out this test to see if the message was posted correctly @@ -349,8 +388,6 @@ pub async fn test_execute_order_fallback() { ); let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - - println!("Solver balance before placing initial offer: {:?}", solver.get_balance(&testing_context.test_context).await); // Place initial offer using the fallback program let initial_offer_fixture = place_initial_offer_fallback( @@ -378,4 +415,83 @@ pub async fn test_execute_order_fallback() { // Figure out why the solver balance is not increased here println!("Solver balance after executing order: {:?}", solver.get_balance(&testing_context.test_context).await); +} + +#[tokio::test] +pub async fn test_prepare_order_shim_fallback() { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + pre_testing_context.add_verify_shims(); + pre_testing_context.add_post_message_shims(); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0), false); + let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let actors = testing_context.testing_actors; + let payer_signer = actors.owner.keypair(); + let first_test_ft = vaas_test.0.first().unwrap(); + let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + + // Try making initial offer using the shim instruction + let usdc_mint_address = USDC_MINT_ADDRESS; + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = create_all_router_endpoints_test( + &testing_context.test_context, + actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + fixture_accounts.arbitrum_remote_token_messenger, + fixture_accounts.ethereum_remote_token_messenger, + usdc_mint_address, + actors.owner.keypair(), + PROGRAM_ID, + ).await; + let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; + let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + let solver: utils::setup::Solver = actors.solvers[0].clone(); + + let auction_accounts = AuctionAccounts::new( + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + arb_endpoint_address, // From router endpoint pubkey + eth_endpoint_address, // To router endpoint pubkey + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + ); + + let ft_vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + + // Place initial offer using the fallback program + let initial_offer_fixture = place_initial_offer_fallback( + &testing_context.test_context, + &payer_signer, + &PROGRAM_ID, + &CORE_BRIDGE_PROGRAM_ID, + &ft_vaa_data, + solver.clone(), + &auction_accounts, + 1__000_000, // 1 USDC (double underscore for decimal separator) + ).await.expect("Failed to place initial offer"); + + let deposit_vaa_data = first_test_ft.deposit_vaa.clone().vaa_data; + + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture, &payer_signer.pubkey(), &fixture_accounts); + // Try executing the order using the fallback program + let execute_order_fixture = execute_order_fallback( + &testing_context.test_context, + &payer_signer, + &PROGRAM_ID, + solver.clone(), + &execute_order_fallback_accounts, + ).await.expect("Failed to execute order"); + + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &payer_signer, &deposit_vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; + let arbitrum_token_messenger_data = testing_context.test_context.borrow_mut().banks_client.get_account(fixture_accounts.arbitrum_remote_token_messenger).await.unwrap().unwrap().data; + let ethereum_token_messenger_data = testing_context.test_context.borrow_mut().banks_client.get_account(fixture_accounts.ethereum_remote_token_messenger).await.unwrap().unwrap().data; + let source_remote_token_messenger = RemoteTokenMessenger::try_deserialize(&mut arbitrum_token_messenger_data.as_ref()).unwrap(); + let destination_remote_token_messenger = RemoteTokenMessenger::try_deserialize(&mut ethereum_token_messenger_data.as_ref()).unwrap(); + let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new(encoded_cctp_message, cctp_attestation) + let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new(&payer_signer, &fixture_accounts, &execute_order_fallback_accounts, &initial_offer_fixture, &initialize_fixture, &arb_endpoint_address, ð_endpoint_address, &usdc_mint_address, &cctp_message_decoded, &guardian_set_pubkey, &guardian_signatures_pubkey); + prepare_order_response_cctp_shim(&testing_context.test_context, &payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, &PROGRAM_ID).await; + } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/cctp_message.rs b/solana/programs/matching-engine/tests/utils/cctp_message.rs new file mode 100644 index 000000000..263934ac5 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/cctp_message.rs @@ -0,0 +1,607 @@ +use anchor_lang::prelude::*; +use common::wormhole_cctp_solana::cctp::message_transmitter_program::MessageTransmitterConfig; +use num_traits::FromBytes; +use solana_sdk::keccak; +use std::fmt::Display; +use solana_program::keccak::{Hash, Hasher}; +use secp256k1::SecretKey as SecpSecretKey; +use std::str::FromStr; +use std::rc::Rc; +use std::cell::RefCell; +use solana_program_test::ProgramTestContext; +use common::wormhole_cctp_solana::cctp::{MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID}; + + +use crate::utils::ETHEREUM_USDC_ADDRESS; + +use super::{Chain, CHAIN_TO_DOMAIN, GUARDIAN_SECRET_KEY}; + +// Imported from https://github.com/circlefin/solana-cctp-contracts.git rev = "4477f88" + +#[error_code] +pub enum MathError { + #[msg("Overflow in arithmetic operation")] + MathOverflow, + #[msg("Underflow in arithmetic operation")] + MathUnderflow, + #[msg("Error in division operation")] + ErrorInDivision, +} + +#[error_code] +pub enum MessageTransmitterError { + #[msg("Invalid authority")] + InvalidAuthority, + #[msg("Instruction is not allowed at this time")] + ProgramPaused, + #[msg("Invalid message transmitter state")] + InvalidMessageTransmitterState, + #[msg("Invalid signature threshold")] + InvalidSignatureThreshold, + #[msg("Signature threshold already set")] + SignatureThresholdAlreadySet, + #[msg("Invalid owner")] + InvalidOwner, + #[msg("Invalid pauser")] + InvalidPauser, + #[msg("Invalid attester manager")] + InvalidAttesterManager, + #[msg("Invalid attester")] + InvalidAttester, + #[msg("Attester already enabled")] + AttesterAlreadyEnabled, + #[msg("Too few enabled attesters")] + TooFewEnabledAttesters, + #[msg("Signature threshold is too low")] + SignatureThresholdTooLow, + #[msg("Attester already disabled")] + AttesterAlreadyDisabled, + #[msg("Message body exceeds max size")] + MessageBodyLimitExceeded, + #[msg("Invalid destination caller")] + InvalidDestinationCaller, + #[msg("Invalid message recipient")] + InvalidRecipient, + #[msg("Sender is not permitted")] + SenderNotPermitted, + #[msg("Invalid source domain")] + InvalidSourceDomain, + #[msg("Invalid destination domain")] + InvalidDestinationDomain, + #[msg("Invalid message version")] + InvalidMessageVersion, + #[msg("Invalid used nonces account")] + InvalidUsedNoncesAccount, + #[msg("Invalid recipient program")] + InvalidRecipientProgram, + #[msg("Invalid nonce")] + InvalidNonce, + #[msg("Nonce already used")] + NonceAlreadyUsed, + #[msg("Message is too short")] + MessageTooShort, + #[msg("Malformed message")] + MalformedMessage, + #[msg("Invalid signature order or dupe")] + InvalidSignatureOrderOrDupe, + #[msg("Invalid attester signature")] + InvalidAttesterSignature, + #[msg("Invalid attestation length")] + InvalidAttestationLength, + #[msg("Invalid signature recovery ID")] + InvalidSignatureRecoveryId, + #[msg("Invalid signature S value")] + InvalidSignatureSValue, + #[msg("Invalid message hash")] + InvalidMessageHash, +} + +#[error_code] +pub enum TokenMessengerError { + #[msg("Invalid authority")] + InvalidAuthority, + #[msg("Invalid token messenger state")] + InvalidTokenMessengerState, + #[msg("Invalid token messenger")] + InvalidTokenMessenger, + #[msg("Invalid owner")] + InvalidOwner, + #[msg("Malformed message")] + MalformedMessage, + #[msg("Invalid message body version")] + InvalidMessageBodyVersion, + #[msg("Invalid amount")] + InvalidAmount, + #[msg("Invalid destination domain")] + InvalidDestinationDomain, + #[msg("Invalid destination caller")] + InvalidDestinationCaller, + #[msg("Invalid mint recipient")] + InvalidMintRecipient, + #[msg("Invalid sender")] + InvalidSender, + #[msg("Invalid token pair")] + InvalidTokenPair, + #[msg("Invalid token mint")] + InvalidTokenMint, +} + +//https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/state.rs#L35-L38 +#[derive(Debug, InitSpace)] +pub struct CctpRemoteTokenMessenger { + pub domain: u32, // Big endian + pub token_messenger: Pubkey, +} + +impl CctpRemoteTokenMessenger { + pub fn new(domain: u32, token_messenger: Pubkey) -> Self { + Self { domain, token_messenger } + } + + pub fn try_deserialize(data: &[u8]) -> Result { + require_eq!(data.len(), 36, TokenMessengerError::MalformedMessage); + let domain = u32::from_be_bytes(data[0..4].try_into().unwrap()); + let token_messenger = Pubkey::try_from(&data[4..36]).unwrap(); + Ok(Self { domain, token_messenger }) + } +} + +// https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/message-transmitter/src/message.rs#L30 +#[derive(Clone, Debug)] +pub struct Message<'a> { + data: &'a [u8], +} + +pub fn checked_add(arg1: T, arg2: T) -> Result +where + T: num_traits::PrimInt + Display, +{ + if let Some(res) = arg1.checked_add(&arg2) { + Ok(res) + } else { + msg!("Error: Overflow in {} + {}", arg1, arg2); + err!(MathError::MathOverflow) + } +} + +impl<'a> Message<'a> { + // Indices of each field in the message + const VERSION_INDEX: usize = 0; + const SOURCE_DOMAIN_INDEX: usize = 4; + const DESTINATION_DOMAIN_INDEX: usize = 8; + const NONCE_INDEX: usize = 12; + const SENDER_INDEX: usize = 20; + const RECIPIENT_INDEX: usize = 52; + const DESTINATION_CALLER_INDEX: usize = 84; + const MESSAGE_BODY_INDEX: usize = 116; + + /// Validates source array size and returns a new message + pub fn new(expected_version: u32, message_bytes: &'a [u8]) -> Result { + require_gte!( + message_bytes.len(), + Self::MESSAGE_BODY_INDEX + ); + let message = Self { + data: message_bytes, + }; + require_eq!( + expected_version, + message.version()?, + ); + Ok(message) + } + + pub fn serialized_len(message_body_len: usize) -> Result { + checked_add(Self::MESSAGE_BODY_INDEX, message_body_len) + } + + #[allow(clippy::too_many_arguments)] + /// Serializes given fields into a message + pub fn format_message( + version: u32, + local_domain: u32, + destination_domain: u32, + nonce: u64, + sender: &Pubkey, + recipient: &Pubkey, + destination_caller: &Pubkey, + message_body: &Vec, + ) -> Result> { + let mut output = vec![0; Message::serialized_len(message_body.len())?]; + + output[Self::VERSION_INDEX..Self::SOURCE_DOMAIN_INDEX] + .copy_from_slice(&version.to_be_bytes()); + output[Self::SOURCE_DOMAIN_INDEX..Self::DESTINATION_DOMAIN_INDEX] + .copy_from_slice(&local_domain.to_be_bytes()); + output[Self::DESTINATION_DOMAIN_INDEX..Self::NONCE_INDEX] + .copy_from_slice(&destination_domain.to_be_bytes()); + output[Self::NONCE_INDEX..Self::SENDER_INDEX].copy_from_slice(&nonce.to_be_bytes()); + output[Self::SENDER_INDEX..Self::RECIPIENT_INDEX].copy_from_slice(sender.as_ref()); + output[Self::RECIPIENT_INDEX..Self::DESTINATION_CALLER_INDEX] + .copy_from_slice(recipient.as_ref()); + output[Self::DESTINATION_CALLER_INDEX..Self::MESSAGE_BODY_INDEX] + .copy_from_slice(destination_caller.as_ref()); + if !message_body.is_empty() { + output[Self::MESSAGE_BODY_INDEX..].copy_from_slice(message_body.as_slice()); + } + + Ok(output) + } + + /// Returns Keccak hash of the message + pub fn hash(&self) -> Hash { + let mut hasher = Hasher::default(); + hasher.hash(self.data); + hasher.result() + } + + /// Returns version field + pub fn version(&self) -> Result { + self.read_integer::(Self::VERSION_INDEX) + } + + /// Returns sender field + pub fn sender(&self) -> Result { + self.read_pubkey(Self::SENDER_INDEX) + } + + /// Returns recipient field + pub fn recipient(&self) -> Result { + self.read_pubkey(Self::RECIPIENT_INDEX) + } + + /// Returns source_domain field + pub fn source_domain(&self) -> Result { + self.read_integer::(Self::SOURCE_DOMAIN_INDEX) + } + + /// Returns destination_domain field + pub fn destination_domain(&self) -> Result { + self.read_integer::(Self::DESTINATION_DOMAIN_INDEX) + } + + /// Returns destination_caller field + pub fn destination_caller(&self) -> Result { + self.read_pubkey(Self::DESTINATION_CALLER_INDEX) + } + + /// Returns nonce field + pub fn nonce(&self) -> Result { + self.read_integer::(Self::NONCE_INDEX) + } + + /// Returns message_body field + pub fn message_body(&self) -> &[u8] { + &self.data[Self::MESSAGE_BODY_INDEX..] + } + + //////////////////// + // private helpers + + /// Reads integer field at the given offset + fn read_integer(&self, index: usize) -> Result + where + T: num_traits::PrimInt + FromBytes + Display, + &'a ::Bytes: TryFrom<&'a [u8]> + 'a, + { + Ok(T::from_be_bytes( + self.data[index..checked_add(index, std::mem::size_of::())?] + .try_into() + .map_err(|_| MessageTransmitterError::MalformedMessage)?, + )) + } + + /// Reads pubkey field at the given offset + fn read_pubkey(&self, index: usize) -> Result { + Ok(Pubkey::try_from( + &self.data[index..checked_add(index, std::mem::size_of::())?], + ) + .map_err(|_| MessageTransmitterError::MalformedMessage)?) + } +} + +// https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/burn_message.rs#L26 +#[derive(Clone, Debug)] +pub struct BurnMessage<'a> { + data: &'a [u8], +} + +impl<'a> BurnMessage<'a> { + // Indices of each field in the message + const VERSION_INDEX: usize = 0; + const BURN_TOKEN_INDEX: usize = 4; + const MINT_RECIPIENT_INDEX: usize = 36; + const AMOUNT_INDEX: usize = 68; + const MSG_SENDER_INDEX: usize = 100; + // 4 byte version + 32 bytes burnToken + 32 bytes mintRecipient + 32 bytes amount + 32 bytes messageSender + const BURN_MESSAGE_LEN: usize = 132; + // EVM amount is 32 bytes while we use only 8 bytes on Solana + const AMOUNT_OFFSET: usize = 24; + + /// Validates source array size and returns a new message + pub fn new(expected_version: u32, message_bytes: &'a [u8]) -> Result { + require_eq!( + message_bytes.len(), + Self::BURN_MESSAGE_LEN, + TokenMessengerError::MalformedMessage + ); + let message = Self { + data: message_bytes, + }; + require_eq!( + expected_version, + message.version()?, + TokenMessengerError::InvalidMessageBodyVersion + ); + Ok(message) + } + + #[allow(clippy::too_many_arguments)] + /// Serializes given fields into a burn message + pub fn format_message( + version: u32, + burn_token: &Pubkey, + mint_recipient: &Pubkey, + amount: u64, + message_sender: &Pubkey, + ) -> Result> { + let mut output = vec![0; Self::BURN_MESSAGE_LEN]; + + output[Self::VERSION_INDEX..Self::BURN_TOKEN_INDEX].copy_from_slice(&version.to_be_bytes()); + output[Self::BURN_TOKEN_INDEX..Self::MINT_RECIPIENT_INDEX] + .copy_from_slice(burn_token.as_ref()); + output[Self::MINT_RECIPIENT_INDEX..Self::AMOUNT_INDEX] + .copy_from_slice(mint_recipient.as_ref()); + output[(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET)..Self::MSG_SENDER_INDEX] + .copy_from_slice(&amount.to_be_bytes()); + output[Self::MSG_SENDER_INDEX..Self::BURN_MESSAGE_LEN] + .copy_from_slice(message_sender.as_ref()); + + Ok(output) + } + + /// Returns version field + pub fn version(&self) -> Result { + self.read_integer::(Self::VERSION_INDEX) + } + + /// Returns burn_token field + pub fn burn_token(&self) -> Result { + self.read_pubkey(Self::BURN_TOKEN_INDEX) + } + + /// Returns mint_recipient field + pub fn mint_recipient(&self) -> Result { + self.read_pubkey(Self::MINT_RECIPIENT_INDEX) + } + + /// Returns amount field + pub fn amount(&self) -> Result { + require!( + self.data[Self::AMOUNT_INDEX..(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET)] + .iter() + .all(|&x| x == 0), + TokenMessengerError::MalformedMessage + ); + self.read_integer::(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET) + } + + /// Returns message_sender field + pub fn message_sender(&self) -> Result { + self.read_pubkey(Self::MSG_SENDER_INDEX) + } + + //////////////////// + // private helpers + + /// Reads integer field at the given offset + fn read_integer(&self, index: usize) -> Result + where + T: num_traits::PrimInt + FromBytes + Display, + &'a ::Bytes: TryFrom<&'a [u8]> + 'a, + { + Ok(T::from_be_bytes( + self.data[index..checked_add(index, std::mem::size_of::())?] + .try_into() + .map_err(|_| TokenMessengerError::MalformedMessage)?, + )) + } + + /// Reads pubkey field at the given offset + fn read_pubkey(&self, index: usize) -> Result { + Ok(Pubkey::try_from( + &self.data[index..checked_add(index, std::mem::size_of::())?], + ) + .map_err(|_| TokenMessengerError::MalformedMessage)?) + } +} + +pub struct CircleAttester { + // You'll need to define a private key constant similar to GUARDIAN_KEY in TypeScript + guardian_secret_key: SecpSecretKey, +} + +impl CircleAttester { + + pub fn create_attestation(&self, message: &[u8]) -> [u8; 65] { + // Sign the message hash with the guardian key + let secp = secp256k1::SECP256K1; + let digest = keccak::hash(message).to_bytes(); + let msg = secp256k1::Message::from_digest(digest); + let recoverable_signature = secp.sign_ecdsa_recoverable(&msg, &self.guardian_secret_key); + let mut signature_bytes = [0u8; 65]; + // Next 64 bytes are the signature in compact format + let (recovery_id, compact_sig) = recoverable_signature.serialize_compact(); + // Recovery ID goes in byte 65 + signature_bytes[0..64].copy_from_slice(&compact_sig); + signature_bytes[64] = i32::from(recovery_id) as u8; + signature_bytes + } +} + +impl Default for CircleAttester { + fn default() -> Self { + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); + Self { guardian_secret_key } + } +} + +pub struct CctpTokenBurnMessage { + pub destination_cctp_domain: u32, + pub cctp_message: CctpMessage, + pub encoded_cctp_burn_message: Vec, + pub cctp_attestation: Vec, +} + +pub struct CctpMessageHeader { + pub version: u32, + pub source_domain: u32, + pub destination_domain: u32, + pub nonce: u64, + pub sender: [u8; 32], + pub recipient: [u8; 32], + pub destination_caller: [u8; 32], +} + +impl CctpMessageHeader { + pub fn encode(&self) -> Vec { + let mut buf = Vec::with_capacity(116); + buf[0..4].copy_from_slice(&self.version.to_be_bytes()); + buf[4..8].copy_from_slice(&self.source_domain.to_be_bytes()); + buf[8..12].copy_from_slice(&self.destination_domain.to_be_bytes()); + buf[12..20].copy_from_slice(&self.nonce.to_be_bytes()); + buf[20..52].copy_from_slice(&self.sender); + buf[52..84].copy_from_slice(&self.recipient); + buf[84..116].copy_from_slice(&self.destination_caller); + buf + } +} + + +pub struct CctpMessageBody { + pub version: u32, + pub burn_token_address: [u8; 32], + pub mint_recipient: [u8; 32], + pub amount: [u8; 32], // EVM amount as uint256 now in big endian byte format + pub message_sender: [u8; 32], +} + +impl CctpMessageBody { + pub fn encode(&self) -> Vec { + let mut buf = Vec::with_capacity(132); + buf[0..4].copy_from_slice(&self.version.to_be_bytes()); + buf[4..36].copy_from_slice(&self.burn_token_address); + buf[36..68].copy_from_slice(&self.mint_recipient); + buf[68..100].copy_from_slice(&self.amount); + buf[100..132].copy_from_slice(&self.message_sender); + buf + } +} +impl From<&BurnMessage<'_>> for CctpMessageBody { + + fn from(value: &BurnMessage) -> Self { + Self { version: value.version().expect("Version not found"), burn_token_address: value.burn_token().expect("Burn token address not found").to_bytes(), mint_recipient: value.mint_recipient().expect("Mint recipient not found").to_bytes(), amount: to_uint256_bytes(value.amount().expect("Amount not found")), message_sender: value.message_sender().expect("Message sender not found").to_bytes() } + } + +} + +fn to_uint256_bytes(amount: u64) -> [u8; 32] { + let mut buf = [0u8; 32]; + buf[32-8..].copy_from_slice(&amount.to_be_bytes()); + buf +} + +pub struct CctpMessage { + pub header: CctpMessageHeader, + pub body: CctpMessageBody, +} + +impl CctpMessage { + pub fn encode(&self) -> Vec { + let mut buf = Vec::with_capacity(116 + 132); + buf[0..116].copy_from_slice(&self.header.encode()); + buf[116..].copy_from_slice(&self.body.encode()); + buf + } +} + + +pub async fn craft_cctp_token_burn_message( + test_ctx: &Rc>, + source_cctp_domain: u32, + cctp_nonce: u64, + amount: u64, // Only allows for 8 byte amounts for now. If we want larger amount support, we can change this to uint256. + message_transmitter_config_pubkey: &Pubkey, + remote_token_messenger_pubkey: &Pubkey, + cctp_mint_recipient: &Pubkey, + custodian_address: &Pubkey, +) -> Result { + let destination_cctp_domain = CHAIN_TO_DOMAIN[Chain::Solana as usize].1; // Hard code solana as destination domain + assert_eq!(destination_cctp_domain, 5); + let message_transmitter_config_data = test_ctx.borrow_mut().banks_client.get_account(*message_transmitter_config_pubkey).await.expect("Failed to fetch account").expect("Account not found").data; + let message_transmitter_config = MessageTransmitterConfig::try_deserialize(&mut &message_transmitter_config_data[..]).expect("Failed to deserialize message transmitter config"); + let cctp_header_version = message_transmitter_config.version; + let local_domain = message_transmitter_config.local_domain; + assert_eq!(local_domain, destination_cctp_domain); + let remote_token_messenger_data = test_ctx.borrow_mut().banks_client.get_account(*remote_token_messenger_pubkey).await.expect("Failed to fetch account").expect("Account not found").data; + let remote_token_messenger = CctpRemoteTokenMessenger::try_deserialize(&mut &remote_token_messenger_data[..]).expect("Could not deserialize remote token messenger"); + let source_token_messenger = remote_token_messenger.token_messenger; + let burn_token_address = ethereum_address_to_universal(ETHEREUM_USDC_ADDRESS); + + let burn_message_vec = BurnMessage::format_message( + 0, + &Pubkey::try_from_slice(&burn_token_address).unwrap(), + &cctp_mint_recipient, + amount, + &Pubkey::try_from_slice(&[0u8; 32]).unwrap(), + )?; + + let burn_message = BurnMessage::new(0, &burn_message_vec).unwrap(); + + let cctp_message_body = CctpMessageBody::from(&burn_message); + + let cctp_message_header = CctpMessageHeader { + version: cctp_header_version, + source_domain: source_cctp_domain, + destination_domain: destination_cctp_domain, + nonce: cctp_nonce, + sender: source_token_messenger.to_bytes(), + recipient: TOKEN_MESSENGER_MINTER_PROGRAM_ID.to_bytes(), + destination_caller: custodian_address.to_bytes(), + }; + + assert_eq!(cctp_message_body.encode().len(), burn_message_vec.len(), "CCTP message body length mismatch"); + assert_eq!(cctp_message_body.encode(), burn_message_vec, "CCTP message body mismatch"); + + let cctp_message = CctpMessage { + header: cctp_message_header, + body: cctp_message_body, + }; + + let encoded_cctp_message = cctp_message.encode(); + + let cctp_attestation = CircleAttester::default().create_attestation(&encoded_cctp_message); + + Ok(CctpTokenBurnMessage { + destination_cctp_domain, + cctp_message, + encoded_cctp_burn_message: encoded_cctp_message, + cctp_attestation: cctp_attestation.to_vec(), + }) +} + +pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { + // Remove '0x' prefix if present + let address_str = eth_address.strip_prefix("0x").unwrap_or(eth_address); + + // Decode the hex string to bytes + let mut address_bytes = [0u8; 20]; // Ethereum addresses are 20 bytes + hex::decode_to_slice(address_str, &mut address_bytes as &mut [u8]) + .expect("Invalid Ethereum address format"); + + // Create a 32-byte array with leading zeros (Ethereum addresses are padded with zeros on the left) + let mut universal_address = [0u8; 32]; + universal_address[12..32].copy_from_slice(&address_bytes); + + universal_address +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 1859b1696..ec6cb4913 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -107,7 +107,6 @@ pub fn get_player_one_keypair() -> Keypair { #[allow(dead_code)] pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111111111111111111111111115"); -#[allow(dead_code)] pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // Chain to cctp domain mapping diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/utils/initialize.rs index 2fac922a0..1bff89411 100644 --- a/solana/programs/matching-engine/tests/utils/initialize.rs +++ b/solana/programs/matching-engine/tests/utils/initialize.rs @@ -24,6 +24,7 @@ use super::super::TestingContext; pub struct InitializeAddresses { pub custodian_address: Pubkey, pub auction_config_address: Pubkey, + pub cctp_mint_recipient: Pubkey, } pub struct InitializeFixture { @@ -170,6 +171,7 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu let initialize_addresses = InitializeAddresses { custodian_address: custodian, auction_config_address: auction_config, + cctp_mint_recipient: cctp_mint_recipient, }; InitializeFixture { test_context, custodian: custodian_data, addresses: initialize_addresses } } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index 3b6c441ad..b354275cc 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -16,4 +16,6 @@ pub mod auction; pub mod setup; pub mod shims; pub mod shims_execute_order; +pub mod shims_prepare_order_response; +pub mod cctp_message; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index 40fcfa0a1..e6ad05cec 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -32,6 +32,10 @@ use matching_engine::fallback::initialise_fast_market_order::{ InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, }; +use matching_engine::fallback::close_fast_market_order::{ + CloseFastMarketOrder as CloseFastMarketOrderFallback, + CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, +}; use wormhole_svm_definitions::borsh::GuardianSignatures; #[allow(dead_code)] @@ -362,14 +366,14 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc>, refund_recipient_keypair: &Rc, program_id: &Pubkey, fast_market_order_address: &Pubkey) { + let recent_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + let close_fast_market_order_ix = CloseFastMarketOrderFallback { + program_id: program_id, + accounts: CloseFastMarketOrderFallbackAccounts { + fast_market_order: fast_market_order_address, + refund_recipient: &refund_recipient_keypair.pubkey(), + }, + }.instruction(); + + let transaction = Transaction::new_signed_with_payer(&[close_fast_market_order_ix], Some(&refund_recipient_keypair.pubkey()), &[refund_recipient_keypair], recent_blockhash); + test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to close fast market order"); +} + pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::PostedVaaData, refund_recipient: Pubkey) -> (FastMarketOrderState, super::vaa::PostedVaaData) { let vaa_data = super::vaa::PostedVaaData { consistency_level: vaa_data.consistency_level, diff --git a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs index 28580c343..027fe1802 100644 --- a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs @@ -51,6 +51,15 @@ pub struct ExecuteOrderFallbackFixture { pub cctp_message: Pubkey, pub post_message_sequence: Pubkey, pub post_message_message: Pubkey, + pub accounts: ExecuteOrderFallbackFixtureAccounts, +} + +pub struct ExecuteOrderFallbackFixtureAccounts { + pub local_token: Pubkey, + pub token_messenger: Pubkey, + pub remote_token_messenger: Pubkey, + pub token_messenger_minter_sender_authority: Pubkey, + pub token_messenger_minter_event_authority: Pubkey, } pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts) -> Result { @@ -120,5 +129,12 @@ pub async fn execute_order_fallback(test_ctx: &Rc>, cctp_message, post_message_sequence, post_message_message, + accounts: ExecuteOrderFallbackFixtureAccounts { + local_token, + token_messenger, + remote_token_messenger, + token_messenger_minter_sender_authority, + token_messenger_minter_event_authority : *token_messenger_minter_event_authority, + }, }) } \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs new file mode 100644 index 000000000..b4a5fd2d9 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs @@ -0,0 +1,251 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; +use common::messages::SlowOrderResponse; +use common::wormhole_cctp_solana::messages::Deposit; +use common::wormhole_cctp_solana::utils::CctpMessage; +use matching_engine::fallback::prepare_order_response::{ + DepositMessage, FinalizedVaaMessage, PrepareOrderResponseCctpShim as PrepareOrderResponseCctpShimIx, PrepareOrderResponseCctpShimAccounts, PrepareOrderResponseCctpShimData}; +use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; +use solana_program_test::ProgramTestContext; +use solana_sdk::signature::Keypair; +use solana_sdk::signer::Signer; +use solana_sdk::transaction::Transaction; +use wormhole_io::TypePrefixedPayload; +use std::rc::Rc; +use std::cell::RefCell; +use common::wormhole_cctp_solana::cctp::{MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID}; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +use super::account_fixtures::FixtureAccounts; +use super::initialize::InitializeFixture; +use super::shims::PlaceInitialOfferShimFixture; +use super::shims_execute_order::ExecuteOrderFallbackFixture; + +pub struct PrepareOrderResponseShimAccountsFixture { + pub signer: Pubkey, + pub custodian: Pubkey, + pub fast_market_order: Pubkey, + pub from_endpoint: Pubkey, + pub to_endpoint: Pubkey, + pub base_fee_token: Pubkey, + pub usdc: Pubkey, + pub cctp_mint_recipient: Pubkey, + pub cctp_message_transmitter_authority: Pubkey, + pub cctp_message_transmitter_config: Pubkey, + pub cctp_used_nonces: Pubkey, + pub cctp_message_transmitter_event_authority: Pubkey, + pub cctp_token_messenger: Pubkey, + pub cctp_remote_token_messenger: Pubkey, + pub cctp_token_minter: Pubkey, + pub cctp_local_token: Pubkey, + pub cctp_token_messenger_minter_custody_token: Pubkey, + pub cctp_token_messenger_minter_program: Pubkey, + pub cctp_message_transmitter_program: Pubkey, + pub cctp_token_pair: Pubkey, + pub cctp_token_messenger_minter_event_authority: Pubkey, + pub guardian_set: Pubkey, + pub guardian_set_signatures: Pubkey, +} + +struct UsedNonces; + +impl UsedNonces { + pub const MAX_NONCES: u64 = 6400; + pub fn address(remote_domain: u32, nonce: u64) -> (Pubkey, u8) { + let first_nonce = (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket + let remote_domain_converted = remote_domain.to_string(); + let first_nonce_converted = first_nonce.to_string(); + Pubkey::find_program_address(&[ + b"used_nonces", + remote_domain_converted.as_bytes(), + first_nonce_converted.as_bytes(), + ], &MESSAGE_TRANSMITTER_PROGRAM_ID) + } +} + +impl PrepareOrderResponseShimAccountsFixture { + pub fn new(signer: &Pubkey, + fixture_accounts: &FixtureAccounts, + execute_order_fixture: &ExecuteOrderFallbackFixture, + initial_offer_fixture: &PlaceInitialOfferShimFixture, + initialize_fixture: &InitializeFixture, + to_router_endpoint: &Pubkey, + from_router_endpoint: &Pubkey, + usdc_mint_address: &Pubkey, + cctp_message_decoded: &CctpMessageDecoded, + guardian_set: &Pubkey, + guardian_set_signatures: &Pubkey, + ) -> Self { + let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let cctp_message_transmitter_authority = Pubkey::find_program_address(&[b"message_transmitter_authority", &TOKEN_MESSENGER_MINTER_PROGRAM_ID.as_ref()], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let (cctp_used_nonces_pda, _cctp_used_nonces_bump) = UsedNonces::address(cctp_message_decoded.source_domain, cctp_message_decoded.nonce); + Self { + signer: signer.clone(), + custodian: initialize_fixture.get_custodian_address(), + fast_market_order: initial_offer_fixture.fast_market_order_address, + from_endpoint: from_router_endpoint.clone(), + to_endpoint: to_router_endpoint.clone(), + base_fee_token: usdc_mint_address.clone(), // Change this to the solver's address? + usdc: usdc_mint_address.clone(), + cctp_mint_recipient: initialize_fixture.addresses.cctp_mint_recipient.clone(), + cctp_message_transmitter_authority: cctp_message_transmitter_authority.clone(), + cctp_message_transmitter_config: fixture_accounts.message_transmitter_config.clone(), + cctp_used_nonces: cctp_used_nonces_pda.clone(), + cctp_message_transmitter_event_authority: cctp_message_transmitter_event_authority.clone(), + cctp_token_messenger: fixture_accounts.arbitrum_remote_token_messenger.clone(), + cctp_remote_token_messenger: fixture_accounts.ethereum_remote_token_messenger.clone(), + cctp_token_minter: fixture_accounts.token_minter.clone(), + cctp_local_token: fixture_accounts.usdc_local_token.clone(), + cctp_token_pair: fixture_accounts.usdc_token_pair.clone(), + cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), + cctp_token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, + cctp_message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, + cctp_token_messenger_minter_event_authority: execute_order_fixture.accounts.token_messenger_minter_event_authority.clone(), + guardian_set: guardian_set.clone(), + guardian_set_signatures: guardian_set_signatures.clone(), + } + } +} + +pub struct CctpMessageDecoded { + pub nonce: u64, + pub source_domain: u32, +} + +pub struct PrepareOrderResponseShimDataFixture { + pub encoded_cctp_message: Vec, + pub cctp_attestation: Vec, + pub finalized_vaa_message_sequence: u64, + pub finalized_vaa_message_timestamp: u32, + pub finalized_vaa_message_emitter_chain: u16, + pub finalized_vaa_message_emitter_address: [u8; 32], + pub finalized_vaa_message_base_fee: u64, + pub deposit_message: DepositMessage, + pub fast_market_order: FastMarketOrderState, + pub guardian_set_bump: u8, +} + +impl PrepareOrderResponseShimDataFixture { + pub fn new( + encoded_cctp_message: Vec, + cctp_attestation: Vec, + deposit_vaa_data: &super::vaa::PostedVaaData, + deposit: &Deposit, + deposit_base_fee: u64, + fast_market_order: &FastMarketOrderState, + guardian_set_bump: u8, + ) -> Self { + let deposit_message = DepositMessage { + token_address: deposit.token_address, + amount: deposit.amount.to_le_bytes(), + source_cctp_domain: deposit.source_cctp_domain, + destination_cctp_domain: deposit.destination_cctp_domain, + cctp_nonce: deposit.cctp_nonce, + burn_source: deposit.burn_source, + mint_recipient: deposit.mint_recipient, + digest: deposit_vaa_data.digest(), + payload_len: deposit_vaa_data.payload.len() as u16, + payload: deposit_vaa_data.payload.clone(), + }; + Self { + encoded_cctp_message, + cctp_attestation, + finalized_vaa_message_sequence: deposit_vaa_data.sequence, + finalized_vaa_message_timestamp: deposit_vaa_data.vaa_time, + finalized_vaa_message_emitter_chain: deposit_vaa_data.emitter_chain, + finalized_vaa_message_emitter_address: deposit_vaa_data.emitter_address, + finalized_vaa_message_base_fee: deposit_base_fee, + deposit_message, + fast_market_order: fast_market_order.clone(), + guardian_set_bump, + } + } + pub fn decode_cctp_message( + &self, + ) -> CctpMessageDecoded { + let cctp_message_decoded = CctpMessage::parse(&self.encoded_cctp_message[..]).unwrap(); + CctpMessageDecoded { + nonce: cctp_message_decoded.nonce(), + source_domain: cctp_message_decoded.source_domain(), + } + } +} + +pub async fn prepare_order_response_cctp_shim( + test_ctx: &Rc>, + payer_signer: &Rc, + accounts: PrepareOrderResponseShimAccountsFixture, + data: PrepareOrderResponseShimDataFixture, + matching_engine_program_id: &Pubkey, +) -> Result<()> { + let prepared_order_response_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + &data.fast_market_order.digest + ]; + + let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, matching_engine_program_id); + + let prepared_custody_token_seeds = [ + matching_engine::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_pda.as_ref(), + ]; + let (prepared_custody_token_pda, prepared_custody_token_bump) = Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); + + let ix_accounts = PrepareOrderResponseCctpShimAccounts { + signer: &accounts.signer, + custodian: &accounts.custodian, + fast_market_order: &accounts.fast_market_order, + from_endpoint: &accounts.from_endpoint, + to_endpoint: &accounts.to_endpoint, + prepared_order_response: &prepared_order_response_pda, + prepared_custody_token: &prepared_custody_token_pda, + base_fee_token: &accounts.base_fee_token, + usdc: &accounts.usdc, + cctp_mint_recipient: &accounts.cctp_mint_recipient, + cctp_message_transmitter_authority: &accounts.cctp_message_transmitter_authority, + cctp_message_transmitter_config: &accounts.cctp_message_transmitter_config, + cctp_used_nonces: &accounts.cctp_used_nonces, + cctp_message_transmitter_event_authority: &accounts.cctp_message_transmitter_event_authority, + cctp_token_messenger: &accounts.cctp_token_messenger, + cctp_remote_token_messenger: &accounts.cctp_remote_token_messenger, + cctp_token_minter: &accounts.cctp_token_minter, + cctp_local_token: &accounts.cctp_local_token, + cctp_token_pair: &accounts.cctp_token_pair, + cctp_token_messenger_minter_event_authority: &accounts.cctp_token_messenger_minter_event_authority, + cctp_token_messenger_minter_custody_token: &accounts.cctp_token_messenger_minter_custody_token, + cctp_token_messenger_minter_program: &accounts.cctp_token_messenger_minter_program, + cctp_message_transmitter_program: &accounts.cctp_message_transmitter_program, + guardian_set: &accounts.guardian_set, + guardian_set_signatures: &accounts.guardian_set_signatures, + verify_shim_program: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + token_program: &spl_token::ID, + system_program: &solana_program::system_program::ID, + }; + + let finalized_vaa_message = FinalizedVaaMessage { + vaa_sequence: data.finalized_vaa_message_sequence, + vaa_timestamp: data.finalized_vaa_message_timestamp, + vaa_emitter_chain: data.finalized_vaa_message_emitter_chain, + vaa_emitter_address: data.finalized_vaa_message_emitter_address, + base_fee: data.finalized_vaa_message_base_fee, + deposit_message: data.deposit_message, + guardian_set_bump: data.guardian_set_bump, + }; + + let data = PrepareOrderResponseCctpShimData { + encoded_cctp_message: data.encoded_cctp_message, + cctp_attestation: data.cctp_attestation, + finalized_vaa_message, + }; + + let prepare_order_response_cctp_shim_ix = PrepareOrderResponseCctpShimIx { + program_id: matching_engine_program_id, + accounts: ix_accounts, + data, + }.instruction(); + + let recent_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new latest blockhash"); + let transaction = Transaction::new_signed_with_payer(&[prepare_order_response_cctp_shim_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); + test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to process prepare order response cctp shim"); + Ok(()) +} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index ec9e12926..95c0811b3 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -57,6 +57,7 @@ pub struct PostedVaaData { /// Message payload pub payload: Vec, + } impl DataDiscriminator for PostedVaaData { @@ -155,11 +156,18 @@ where Ok(data) } +#[derive(Clone)] +pub enum PayloadDeserialized { + Deposit(Deposit), + FastTransfer(FastMarketOrder), +} + #[derive(Clone)] pub struct TestVaa { pub kind: TestVaaKind, pub vaa_pubkey: Pubkey, pub vaa_data: PostedVaaData, + pub payload_deserialized: Option, } impl TestVaa { @@ -232,9 +240,9 @@ impl TestFastTransfer { create_deposit_and_fast_transfer_params.verify(); let deposit_params = create_deposit_and_fast_transfer_params.deposit_params; let create_fast_transfer_params = create_deposit_and_fast_transfer_params.fast_transfer_params; - let (deposit_vaa_pubkey, deposit_vaa_data) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient, deposit_params.amount, deposit_params.base_fee); - let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence, create_fast_transfer_params.amount_in, create_fast_transfer_params.min_amount_out, create_fast_transfer_params.max_fee, create_fast_transfer_params.init_auction_fee); - Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data } } + let (deposit_vaa_pubkey, deposit_vaa_data, deposit) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient, deposit_params.amount, deposit_params.base_fee); + let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data, fast_market_order) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence, create_fast_transfer_params.amount_in, create_fast_transfer_params.min_amount_out, create_fast_transfer_params.max_fee, create_fast_transfer_params.init_auction_fee); + Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data, payload_deserialized: Some(PayloadDeserialized::Deposit(deposit)) }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data, payload_deserialized: Some(PayloadDeserialized::FastTransfer(fast_market_order)) } } } pub fn add_to_test(&self, program_test:&mut ProgramTest) { @@ -261,7 +269,7 @@ impl TestFastTransfer { } } -pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, amount: i32, base_fee: u64) -> (Pubkey, PostedVaaData) { +pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, amount: i32, base_fee: u64) -> (Pubkey, PostedVaaData, Deposit) { let slow_order_response = SlowOrderResponse { base_fee, @@ -285,10 +293,10 @@ pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; - (vaa_address, posted_vaa_data) + (vaa_address, posted_vaa_data, deposit) } -pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, amount_in: u64, min_amount_out: u64, max_fee: u64, init_auction_fee: u64) -> (Pubkey, PostedVaaData) { +pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, amount_in: u64, min_amount_out: u64, max_fee: u64, init_auction_fee: u64) -> (Pubkey, PostedVaaData, FastMarketOrder) { // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); // Implements TypePrefixedPayload @@ -311,7 +319,7 @@ pub fn create_fast_transfer_message(start_timestamp: Option, source_address let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; - (vaa_address, posted_vaa_data) + (vaa_address, posted_vaa_data, fast_market_order) } pub struct TestFastTransfers(pub Vec); From 3cba0ac53c83c1b470e8e101586493c51b0bfd8c Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 19 Mar 2025 18:00:28 +0000 Subject: [PATCH 021/112] [working] with big cleanup --- .../src/fallback/processor/burn_and_post.rs | 29 +- .../processor/close_fast_market_order.rs | 9 +- .../src/fallback/processor/create_account.rs | 4 +- .../src/fallback/processor/errors.rs | 2 +- .../src/fallback/processor/execute_order.rs | 293 ++++-- .../processor/initialise_fast_market_order.rs | 57 +- .../src/fallback/processor/mod.rs | 10 +- .../fallback/processor/place_initial_offer.rs | 232 +++-- .../processor/prepare_order_response.rs | 415 +++++---- .../fallback/processor/process_instruction.rs | 96 +- solana/programs/matching-engine/src/lib.rs | 11 +- .../auction/offer/place_initial/cctp.rs | 2 +- .../auction/offer/place_initial/cctp_shim.rs | 44 +- .../src/state/fast_market_order.rs | 74 +- .../tests/initialize_integration_tests.rs | 863 ++++++++++++------ .../tests/utils/account_fixtures.rs | 58 +- .../matching-engine/tests/utils/airdrop.rs | 39 +- .../matching-engine/tests/utils/auction.rs | 253 +++-- .../tests/utils/cctp_message.rs | 256 +++--- .../matching-engine/tests/utils/constants.rs | 41 +- .../matching-engine/tests/utils/initialize.rs | 110 ++- .../tests/utils/lookup_table.rs | 113 --- .../matching-engine/tests/utils/mint.rs | 13 +- .../matching-engine/tests/utils/mod.rs | 20 +- .../tests/utils/program_fixtures.rs | 24 +- .../matching-engine/tests/utils/router.rs | 297 ++++-- .../tests/utils/settle_auction.rs | 61 ++ .../matching-engine/tests/utils/setup.rs | 328 ++++++- .../matching-engine/tests/utils/shims.rs | 418 ++++++--- .../tests/utils/shims_execute_order.rs | 181 ++-- .../utils/shims_prepare_order_response.rs | 264 ++++-- .../tests/utils/token_account.rs | 66 +- .../tests/utils/transfer_ownership.rs | 32 - .../matching-engine/tests/utils/vaa.rs | 459 ++++++++-- 34 files changed, 3485 insertions(+), 1689 deletions(-) delete mode 100644 solana/programs/matching-engine/tests/utils/lookup_table.rs create mode 100644 solana/programs/matching-engine/tests/utils/settle_auction.rs delete mode 100644 solana/programs/matching-engine/tests/utils/transfer_ownership.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index f8516782c..dac2e64e8 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -1,11 +1,20 @@ -use common::wormhole_cctp_solana::{cctp::token_messenger_minter_program::cpi::{DepositForBurnWithCallerParams, DepositForBurnWithCaller, deposit_for_burn_with_caller}, cpi::BurnAndPublishArgs}; +use crate::state::Custodian; +use anchor_lang::prelude::*; +use common::wormhole_cctp_solana::{ + cctp::token_messenger_minter_program::cpi::{ + deposit_for_burn_with_caller, DepositForBurnWithCaller, DepositForBurnWithCallerParams, + }, + cpi::BurnAndPublishArgs, +}; use solana_program::program::invoke_signed_unchecked; +use wormhole_svm_definitions::solana::{ + CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID, + POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID, +}; +use wormhole_svm_definitions::{ + find_emitter_sequence_address, find_shim_message_address, solana::Finality, +}; use wormhole_svm_shim::post_message; -use wormhole_svm_definitions::solana::{CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID}; -use anchor_lang::prelude::*; -use wormhole_svm_definitions::{solana::Finality, find_emitter_sequence_address, find_shim_message_address}; -use crate::state::Custodian; - // This is a helper struct to make it easier to pass in the accounts for the post_message instruction. pub struct PostMessageAccounts { @@ -37,8 +46,10 @@ pub struct PostMessageDerivedAccounts { pub fn burn_and_post<'info>( cctp_ctx: CpiContext<'_, '_, '_, 'info, DepositForBurnWithCaller<'info>>, - burn_and_publish_args: BurnAndPublishArgs, post_message_accounts: PostMessageAccounts, - account_infos: &[AccountInfo]) -> Result<()> { + burn_and_publish_args: BurnAndPublishArgs, + post_message_accounts: PostMessageAccounts, + account_infos: &[AccountInfo], +) -> Result<()> { let BurnAndPublishArgs { burn_source: _, destination_caller, @@ -72,7 +83,7 @@ pub fn burn_and_post<'info>( .unwrap(), } .instruction(); - + invoke_signed_unchecked(&post_message_ix, account_infos, &[Custodian::SIGNER_SEEDS])?; // Deposit for burn diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 1feafddb7..6142e0e9b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -1,7 +1,7 @@ +use crate::state::FastMarketOrder; use anchor_lang::prelude::*; -use solana_program::program_error::ProgramError; use solana_program::instruction::Instruction; -use crate::state::FastMarketOrder; +use solana_program::program_error::ProgramError; use super::FallbackMatchingEngineInstruction; @@ -47,7 +47,8 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { return Err(ProgramError::InvalidAccountData.into()); } - let fast_market_order_data = FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + let fast_market_order_data = + FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; if fast_market_order_data.refund_recipient != refund_recipient.key().as_ref() { msg!("Refund recipient (account #2) mismatch"); msg!("Actual:"); @@ -62,4 +63,4 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { **fast_market_order_lamports = 0; Ok(()) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/fallback/processor/create_account.rs b/solana/programs/matching-engine/src/fallback/processor/create_account.rs index 4cdbf9b37..cbdce83d9 100644 --- a/solana/programs/matching-engine/src/fallback/processor/create_account.rs +++ b/solana/programs/matching-engine/src/fallback/processor/create_account.rs @@ -1,9 +1,9 @@ use anchor_lang::prelude::*; use solana_program::{ entrypoint::ProgramResult, + instruction::{AccountMeta, Instruction}, program::invoke_signed_unchecked, system_instruction, - instruction::{AccountMeta, Instruction}, }; pub fn create_account_reliably( payer_key: &Pubkey, @@ -117,4 +117,4 @@ pub fn create_account_reliably( } Ok(()) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/fallback/processor/errors.rs b/solana/programs/matching-engine/src/fallback/processor/errors.rs index dfc9f8f05..991c22f70 100644 --- a/solana/programs/matching-engine/src/fallback/processor/errors.rs +++ b/solana/programs/matching-engine/src/fallback/processor/errors.rs @@ -32,4 +32,4 @@ pub enum FallbackError { #[msg("Invalid program")] InvalidProgram, -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index de0780ca2..3769f699c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,19 +1,21 @@ +use crate::state::{ + Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, + MessageProtocol, RouterEndpoint, +}; +use crate::utils; +use crate::utils::auction::DepositPenalty; use anchor_lang::prelude::*; use anchor_spl::token::{spl_token, TokenAccount}; use common::messages::Fill; -use solana_program::program::invoke_signed_unchecked; -use crate::utils; -use solana_program::instruction::Instruction; -use crate::state::{Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; -use crate::utils::auction::DepositPenalty; use common::wormhole_io::TypePrefixedPayload; +use solana_program::instruction::Instruction; +use solana_program::program::invoke_signed_unchecked; -use super::burn_and_post::{PostMessageAccounts, burn_and_post}; -use super::FallbackMatchingEngineInstruction; +use super::burn_and_post::{burn_and_post, PostMessageAccounts}; use super::errors::FallbackError; +use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; - #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExecuteOrderShimAccounts<'ix> { /// The signer account @@ -33,7 +35,7 @@ pub struct ExecuteOrderShimAccounts<'ix> { pub active_auction_config: &'ix Pubkey, // 6 /// The token account of the auction's best offer pub active_auction_best_offer_token: &'ix Pubkey, // 7 - /// ??? + /// ??? pub executor_token: &'ix Pubkey, // 8 /// The token account of the auction's initial offer pub initial_offer_token: &'ix Pubkey, // 9 @@ -102,15 +104,30 @@ impl<'ix> ExecuteOrderShimAccounts<'ix> { AccountMeta::new(*self.post_message_sequence, false), AccountMeta::new(*self.post_message_message, false), AccountMeta::new(*self.cctp_deposit_for_burn_mint, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, false), - AccountMeta::new(*self.cctp_deposit_for_burn_message_transmitter_config, false), + AccountMeta::new_readonly( + *self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, + false, + ), + AccountMeta::new( + *self.cctp_deposit_for_burn_message_transmitter_config, + false, + ), AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger, false), AccountMeta::new_readonly(*self.cctp_deposit_for_burn_remote_token_messenger, false), AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_minter, false), AccountMeta::new(*self.cctp_deposit_for_burn_local_token, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_event_authority, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger_minter_program, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_message_transmitter_program, false), + AccountMeta::new_readonly( + *self.cctp_deposit_for_burn_token_messenger_minter_event_authority, + false, + ), + AccountMeta::new_readonly( + *self.cctp_deposit_for_burn_token_messenger_minter_program, + false, + ), + AccountMeta::new_readonly( + *self.cctp_deposit_for_burn_message_transmitter_program, + false, + ), AccountMeta::new_readonly(*self.core_bridge_program, false), AccountMeta::new(*self.core_bridge_config, false), AccountMeta::new(*self.core_bridge_fee_collector, false), @@ -174,11 +191,11 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let system_program_account = &accounts[29]; let token_program_account = &accounts[30]; - // Do checks // ------------------------------------------------------------------------------------------------ - let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; + let fast_market_order_zero_copy = + FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; // Bind value for compiler (needed for pda seeds) let active_auction_key = active_auction_account.key(); @@ -194,8 +211,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { common::CCTP_MESSAGE_SEED_PREFIX, active_auction_key.as_ref(), ]; - - let (cctp_message_pda, cctp_message_bump) = Pubkey::find_program_address(&cctp_message_seeds, program_id); + + let (cctp_message_pda, cctp_message_bump) = + Pubkey::find_program_address(&cctp_message_seeds, program_id); if cctp_message_pda != cctp_message_account.key() { msg!("Cctp message seeds are invalid"); return Err(ErrorCode::ConstraintSeeds.into()) @@ -204,7 +222,11 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Check custodian owner if custodian_account.owner != program_id { - msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian_account.owner); + msg!( + "Custodian owner is invalid: expected {}, got {}", + program_id, + custodian_account.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("custodian")); }; @@ -217,18 +239,21 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .map_err(|e: Error| e.with_account_name("custodian")); }; + let fast_market_order_digest = fast_market_order_zero_copy.digest(); // Check fast market order seeds let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, - fast_market_order_zero_copy.digest.as_ref(), + fast_market_order_digest.as_ref(), fast_market_order_zero_copy.refund_recipient.as_ref(), ]; - let (fast_market_order_pda, _fast_market_order_bump) = Pubkey::find_program_address(&fast_market_order_seeds, program_id); + let (fast_market_order_pda, _fast_market_order_bump) = + Pubkey::find_program_address(&fast_market_order_seeds, program_id); if fast_market_order_pda != fast_market_order_account.key() { msg!("Fast market order seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((fast_market_order_pda, fast_market_order_account.key()))); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys((fast_market_order_pda, fast_market_order_account.key())) + }); }; // Check fast market order is owned by the matching engine program @@ -244,9 +269,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("active_auction")); }; - + // Check active auction pda - let mut active_auction = Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; + let mut active_auction = + Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; // Correct way to use create_program_address with existing seeds and bump let active_auction_pda = Pubkey::create_program_address( @@ -255,15 +281,17 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { active_auction.vaa_hash.as_ref(), &[active_auction.bump], ], - program_id - ).map_err(|_| { + program_id, + ) + .map_err(|_| { msg!("Failed to create program address with known bump"); FallbackError::InvalidPda })?; if active_auction_pda != active_auction_account.key() { msg!("Active auction pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((active_auction_pda, active_auction_account.key()))); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys((active_auction_pda, active_auction_account.key())) + }); }; // Check active auction is active @@ -272,7 +300,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { return Err(ErrorCode::ConstraintRaw.into()) .map_err(|e: Error| e.with_account_name("active_auction")); }; - + // Check active auction custody token pda let active_auction_custody_token_pda = Pubkey::create_program_address( &[ @@ -280,19 +308,25 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { active_auction_account.key().as_ref(), &[active_auction.info.as_ref().unwrap().custody_token_bump], ], - program_id - ).map_err(|_| { + program_id, + ) + .map_err(|_| { msg!("Failed to create program address with known bump"); FallbackError::InvalidPda })?; if active_auction_custody_token_pda != active_auction_custody_token_account.key() { msg!("Active auction custody token pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((active_auction_custody_token_pda, active_auction_custody_token_account.key()))); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys(( + active_auction_custody_token_pda, + active_auction_custody_token_account.key(), + )) + }); }; - + // Check active auction config id - let active_auction_config = AuctionConfig::try_deserialize(&mut &active_auction_config_account.data.borrow()[..])?; + let active_auction_config = + AuctionConfig::try_deserialize(&mut &active_auction_config_account.data.borrow()[..])?; if active_auction_config.id != active_auction.info.as_ref().unwrap().config_id { msg!("Active auction config id is invalid"); return Err(MatchingEngineError::AuctionConfigMismatch.into()) @@ -300,34 +334,55 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; // Check active auction best offer token address - if active_auction_best_offer_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { + if active_auction_best_offer_token_account.key() + != active_auction.info.as_ref().unwrap().best_offer_token + { msg!("Active auction best offer token address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((active_auction_best_offer_token_account.key(), active_auction.info.as_ref().unwrap().best_offer_token))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + active_auction_best_offer_token_account.key(), + active_auction.info.as_ref().unwrap().best_offer_token, + )) + }); }; - + // TODO: Done with auction checks, now on to executor token checks if executor_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { msg!("Executor token is not equal to best offer token"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((executor_token_account.key(), active_auction.info.as_ref().unwrap().best_offer_token))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + executor_token_account.key(), + active_auction.info.as_ref().unwrap().best_offer_token, + )) + }); }; // Check initial offer token address - if initial_offer_token_account.key() != active_auction.info.as_ref().unwrap().initial_offer_token { + if initial_offer_token_account.key() + != active_auction.info.as_ref().unwrap().initial_offer_token + { msg!("Initial offer token address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((initial_offer_token_account.key(), active_auction.info.as_ref().unwrap().initial_offer_token))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + initial_offer_token_account.key(), + active_auction.info.as_ref().unwrap().initial_offer_token, + )) + }); }; - + // Check initial participant address if initial_participant_account.key() != active_auction.prepared_by { msg!("Initial participant address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((initial_participant_account.key(), active_auction.prepared_by))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + initial_participant_account.key(), + active_auction.prepared_by, + )) + }); }; - let to_router_endpoint = RouterEndpoint::try_deserialize(&mut &to_router_endpoint_account.data.borrow()[..])?; + let to_router_endpoint = + RouterEndpoint::try_deserialize(&mut &to_router_endpoint_account.data.borrow()[..])?; if to_router_endpoint.protocol != active_auction.target_protocol { msg!("To router endpoint protocol is invalid"); return Err(MatchingEngineError::InvalidEndpoint.into()) @@ -336,35 +391,46 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let destination_cctp_domain = match to_router_endpoint.protocol { MessageProtocol::Cctp { domain } => domain, - _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()) - .map_err(|e: Error| e.with_account_name("to_router_endpoint")), + _ => { + return Err(MatchingEngineError::InvalidCctpEndpoint.into()) + .map_err(|e: Error| e.with_account_name("to_router_endpoint")) + } }; - // Check cctp deposit for burn token messenger minter program address - if cctp_deposit_for_burn_token_messenger_minter_program_account.key() != common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id() { + if cctp_deposit_for_burn_token_messenger_minter_program_account.key() + != common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id() + { msg!("Cctp deposit for burn token messenger minter program address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((cctp_deposit_for_burn_token_messenger_minter_program_account.key(), common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id()))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + cctp_deposit_for_burn_token_messenger_minter_program_account.key(), + common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id(), + )) + }); }; - + // Check cctp deposit for burn message transmitter program address - if cctp_deposit_for_burn_message_transmitter_program_account.key() != common::wormhole_cctp_solana::cctp::message_transmitter_program::id() { + if cctp_deposit_for_burn_message_transmitter_program_account.key() + != common::wormhole_cctp_solana::cctp::message_transmitter_program::id() + { msg!("Cctp deposit for burn message transmitter program address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_pubkeys((cctp_deposit_for_burn_message_transmitter_program_account.key(), common::wormhole_cctp_solana::cctp::message_transmitter_program::id()))); + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_pubkeys(( + cctp_deposit_for_burn_message_transmitter_program_account.key(), + common::wormhole_cctp_solana::cctp::message_transmitter_program::id(), + )) + }); }; - + // End of checks // ------------------------------------------------------------------------------------------------ // Get the fast market order data, without the discriminator let fast_market_order_data = &fast_market_order_account.data.borrow()[8..]; // Deserialise fast market order. Unwrap is safe because the account is owned by the matching engine program. - let fast_market_order = bytemuck::try_from_bytes::( - &fast_market_order_data[..] - ) - .unwrap(); + let fast_market_order = + bytemuck::try_from_bytes::(&fast_market_order_data[..]).unwrap(); // Prepare the execute order (get the user amount, fill, and order executed event) let active_auction_info = active_auction.info.as_ref().unwrap(); @@ -392,7 +458,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { ); let init_auction_fee = fast_market_order.init_auction_fee; - + let user_amount = active_auction_info .amount_in .saturating_sub(active_auction_info.offer_price) @@ -401,8 +467,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Keep track of the remaining amount in the custody token account. Whatever remains will go // to the executor. - - let custody_token = TokenAccount::try_deserialize(&mut &active_auction_custody_token_account.data.borrow()[..])?; + + let custody_token = TokenAccount::try_deserialize( + &mut &active_auction_custody_token_account.data.borrow()[..], + )?; let mut remaining_custodied_amount = custody_token.amount.saturating_sub(user_amount); // Offer price + security deposit was checked in placing the initial offer. @@ -421,9 +489,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Need these seeds in order to transfer tokens and then set authority of auction custody token account to the custodian let auction_signer_seeds = &[ - Auction::SEED_PREFIX, - active_auction.vaa_hash.as_ref(), - &[active_auction.bump], + Auction::SEED_PREFIX, + active_auction.vaa_hash.as_ref(), + &[active_auction.bump], ]; // If the initial offer token account doesn't exist anymore, we have nowhere to send the @@ -443,8 +511,12 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &active_auction_account.key(), &[], init_auction_fee, - ).unwrap(); - msg!("Sending init auction fee {} to initial offer token account", init_auction_fee); + ) + .unwrap(); + msg!( + "Sending init auction fee {} to initial offer token account", + init_auction_fee + ); invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; // Because the initial offer token was paid this fee, we account for it here. remaining_custodied_amount = @@ -473,15 +545,22 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &active_auction_account.key(), &[], deposit_and_fee, - ).unwrap(); - msg!("Sending deposit and fee amount {} to best offer token account", deposit_and_fee); + ) + .unwrap(); + msg!( + "Sending deposit and fee amount {} to best offer token account", + deposit_and_fee + ); invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; } else { // Otherwise, send the deposit and fee to the best offer token. If the best offer token // doesn't exist at this point (which would be unusual), we will reserve these funds // for the executor token. - if utils::checked_deserialize_token_account(active_auction_best_offer_token_account, &common::USDC_MINT) - .is_some() + if utils::checked_deserialize_token_account( + active_auction_best_offer_token_account, + &common::USDC_MINT, + ) + .is_some() { let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, @@ -490,11 +569,14 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &active_auction_account.key(), &[], deposit_and_fee, - ).unwrap(); - msg!("Sending deposit and fee {} to best offer token account", deposit_and_fee); + ) + .unwrap(); + msg!( + "Sending deposit and fee {} to best offer token account", + deposit_and_fee + ); invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; - remaining_custodied_amount = - remaining_custodied_amount.saturating_sub(deposit_and_fee); + remaining_custodied_amount = remaining_custodied_amount.saturating_sub(deposit_and_fee); } // And pay the executor whatever remains in the auction custody token account. @@ -506,8 +588,12 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &active_auction_account.key(), &[], remaining_custodied_amount, - ).unwrap(); - msg!("Sending remaining custodied amount {} to executor token account", remaining_custodied_amount); + ) + .unwrap(); + msg!( + "Sending remaining custodied amount {} to executor token account", + remaining_custodied_amount + ); invoke_signed_unchecked(&instruction, accounts, &[auction_signer_seeds])?; } } @@ -521,7 +607,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { spl_token::instruction::AuthorityType::AccountOwner, &active_auction_account.key(), &[], - ).unwrap(); + ) + .unwrap(); invoke_signed_unchecked(&set_authority_ix, accounts, &[auction_signer_seeds])?; @@ -531,14 +618,23 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { execute_penalty: if penalized { penalty.into() } else { None }, }; + let active_auction_data: &mut [u8] = &mut active_auction_account.data.borrow_mut(); + let mut cursor = std::io::Cursor::new(active_auction_data); + active_auction.try_serialize(&mut cursor).unwrap(); + let fill = Fill { source_chain: active_auction_info.source_chain, order_sender: fast_market_order.sender, redeemer: fast_market_order.redeemer, - redeemer_message: fast_market_order.redeemer_message[..fast_market_order.redeemer_message_length as usize].to_vec().try_into().unwrap(), + redeemer_message: fast_market_order.redeemer_message + [..fast_market_order.redeemer_message_length as usize] + .to_vec() + .try_into() + .unwrap(), }; - let post_message_accounts = PostMessageAccounts::new(custodian_account.key(), signer_account.key()); + let post_message_accounts = + PostMessageAccounts::new(custodian_account.key(), signer_account.key()); // Lets print the auction account balance burn_and_post( @@ -547,20 +643,28 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { burn_token_owner: custodian_account.to_account_info(), payer: signer_account.to_account_info(), - token_messenger_minter_sender_authority: cctp_deposit_for_burn_token_messenger_minter_sender_authority_account.to_account_info(), + token_messenger_minter_sender_authority: + cctp_deposit_for_burn_token_messenger_minter_sender_authority_account + .to_account_info(), burn_token: active_auction_custody_token_account.to_account_info(), - message_transmitter_config: cctp_deposit_for_burn_message_transmitter_config_account.to_account_info(), + message_transmitter_config: + cctp_deposit_for_burn_message_transmitter_config_account.to_account_info(), token_messenger: cctp_deposit_for_burn_token_messenger_account.to_account_info(), - remote_token_messenger: cctp_deposit_for_burn_remote_token_messenger_account.to_account_info(), + remote_token_messenger: cctp_deposit_for_burn_remote_token_messenger_account + .to_account_info(), token_minter: cctp_deposit_for_burn_token_minter_account.to_account_info(), local_token: cctp_deposit_for_burn_local_token_account.to_account_info(), mint: cctp_deposit_for_burn_mint_account.to_account_info(), cctp_message: cctp_message_account.to_account_info(), - message_transmitter_program: cctp_deposit_for_burn_message_transmitter_program_account.to_account_info(), - token_messenger_minter_program: cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), + message_transmitter_program: + cctp_deposit_for_burn_message_transmitter_program_account.to_account_info(), + token_messenger_minter_program: + cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), token_program: token_program_account.to_account_info(), system_program: system_program_account.to_account_info(), - event_authority: cctp_deposit_for_burn_token_messenger_minter_event_authority_account.to_account_info(), + event_authority: + cctp_deposit_for_burn_token_messenger_minter_event_authority_account + .to_account_info(), }, &[ Custodian::SIGNER_SEEDS, @@ -581,7 +685,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { payload: fill.to_vec(), }, post_message_accounts, - accounts + accounts, )?; // Skip emitting the order executed event because we're using a shim @@ -593,9 +697,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { &initial_participant_account.key(), &custodian_account.key(), &[], - ).unwrap(); + ) + .unwrap(); invoke_signed_unchecked(&instruction, accounts, &[Custodian::SIGNER_SEEDS])?; Ok(()) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 7f15c9c5c..89ba6b900 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -1,14 +1,14 @@ use anchor_lang::prelude::*; -use bytemuck::{Pod, Zeroable}; use anchor_lang::Discriminator; -use solana_program::program::invoke_signed_unchecked; +use bytemuck::{Pod, Zeroable}; use solana_program::instruction::Instruction; +use solana_program::program::invoke_signed_unchecked; use super::create_account::create_account_reliably; +use super::errors::FallbackError; use super::FallbackMatchingEngineInstruction; use crate::state::FastMarketOrder as FastMarketOrderState; -use super::errors::FallbackError; pub struct InitialiseFastMarketOrderAccounts<'ix> { pub signer: &'ix Pubkey, @@ -41,7 +41,11 @@ pub struct InitialiseFastMarketOrderData { } impl InitialiseFastMarketOrderData { pub fn new(fast_market_order: FastMarketOrderState, guardian_set_bump: u8) -> Self { - Self { fast_market_order, guardian_set_bump, _padding: [0_u8; 7] } + Self { + fast_market_order, + guardian_set_bump, + _padding: [0_u8; 7], + } } pub fn from_bytes(data: &[u8]) -> Option<&Self> { @@ -65,7 +69,10 @@ impl InitialiseFastMarketOrder<'_> { } } -pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseFastMarketOrderData) -> Result<()> { +pub fn initialise_fast_market_order( + accounts: &[AccountInfo], + data: &InitialiseFastMarketOrderData, +) -> Result<()> { if accounts.len() < 6 { return Err(ErrorCode::AccountNotEnoughKeys.into()); } @@ -76,16 +83,22 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF let _verify_vaa_shim_program = &accounts[4]; let _system_program = &accounts[5]; - let InitialiseFastMarketOrderData { fast_market_order, guardian_set_bump, _padding: _ } = *data; + let InitialiseFastMarketOrderData { + fast_market_order, + guardian_set_bump, + _padding: _, + } = *data; // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ - + let fast_market_order_digest = fast_market_order.digest(); // Did not want to pass in the vaa hash here. So recreated it. let verify_hash_data = { let mut data = vec![]; - data.extend_from_slice(&wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR); + data.extend_from_slice( + &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, + ); data.push(guardian_set_bump); - data.extend_from_slice(&fast_market_order.digest); + data.extend_from_slice(&fast_market_order_digest); data }; let verify_shim_ix = Instruction { @@ -97,10 +110,14 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF data: verify_hash_data, }; // Make the cpi call to verify the shim. - invoke_signed_unchecked(&verify_shim_ix, &[ - guardian_set.to_account_info(), - guardian_set_signatures.to_account_info(), - ], &[])?; + invoke_signed_unchecked( + &verify_shim_ix, + &[ + guardian_set.to_account_info(), + guardian_set_signatures.to_account_info(), + ], + &[], + )?; // ------------------------------------------------------------------------------------------------ // End of cpi call to verify the shim. @@ -111,7 +128,7 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrderState::SEED_PREFIX, - fast_market_order.digest.as_ref(), + fast_market_order_digest.as_ref(), fast_market_order.refund_recipient.as_ref(), ], &program_id, @@ -119,11 +136,12 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF if fast_market_order_pda != fast_market_order_key { msg!("Fast market order pda is invalid"); - return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); + return Err(FallbackError::InvalidPda.into()) + .map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); } let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, - fast_market_order.digest.as_ref(), + fast_market_order_digest.as_ref(), fast_market_order.refund_recipient.as_ref(), &[fast_market_order_bump], ]; @@ -141,7 +159,7 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF // Borrow the account data mutably let mut fast_market_order_account_data = fast_market_order_account.try_borrow_mut_data()?; - + // Write the discriminator to the first 8 bytes let discriminator = FastMarketOrderState::discriminator(); fast_market_order_account_data[0..8].copy_from_slice(&discriminator); @@ -154,7 +172,8 @@ pub fn initialise_fast_market_order(accounts: &[AccountInfo], data: &InitialiseF } // Write the fast_market_order struct to the account - fast_market_order_account_data[8..8 + fast_market_order_bytes.len()].copy_from_slice(fast_market_order_bytes); + fast_market_order_account_data[8..8 + fast_market_order_bytes.len()] + .copy_from_slice(fast_market_order_bytes); Ok(()) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 6838e466d..1a1986f5c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,10 +1,10 @@ pub mod process_instruction; pub use process_instruction::*; -pub mod errors; +pub mod burn_and_post; +pub mod close_fast_market_order; pub mod create_account; -pub mod place_initial_offer; +pub mod errors; pub mod execute_order; -pub mod burn_and_post; -pub mod prepare_order_response; pub mod initialise_fast_market_order; -pub mod close_fast_market_order; \ No newline at end of file +pub mod place_initial_offer; +pub mod prepare_order_response; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 6f7eb3c62..13dd731d2 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,18 +1,21 @@ +use super::create_account::create_account_reliably; +use crate::state::{ + Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, + FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, +}; +use crate::ID as PROGRAM_ID; use anchor_lang::prelude::*; +use anchor_lang::Discriminator; use anchor_spl::token::spl_token; use bytemuck::{Pod, Zeroable}; +use common::TRANSFER_AUTHORITY_SEED_PREFIX; use solana_program::instruction::Instruction; -use solana_program::program::invoke_signed_unchecked; -use super::create_account::create_account_reliably; use solana_program::keccak; -use anchor_lang::Discriminator; +use solana_program::program::invoke_signed_unchecked; use solana_program::program_pack::Pack; -use crate::state::{Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint}; -use common::TRANSFER_AUTHORITY_SEED_PREFIX; -use crate::ID as PROGRAM_ID; -use super::FallbackMatchingEngineInstruction; use super::errors::FallbackError; +use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; #[derive(Debug, Copy, Clone, Pod, Zeroable)] @@ -26,13 +29,18 @@ pub struct PlaceInitialOfferCctpShimData { } impl PlaceInitialOfferCctpShimData { - pub fn new(offer_price: u64, sequence: u64, vaa_time: u32, consistency_level: u8) -> Self { - Self { offer_price, sequence, vaa_time, consistency_level, _padding: [0_u8; 3] } + Self { + offer_price, + sequence, + vaa_time, + consistency_level, + _padding: [0_u8; 3], + } } pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() + bytemuck::try_from_bytes::(data).ok() } } @@ -45,7 +53,7 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub from_endpoint: &'ix Pubkey, pub to_endpoint: &'ix Pubkey, pub fast_market_order: &'ix Pubkey, // Needs initalising. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] - pub auction: &'ix Pubkey, // Needs initalising + pub auction: &'ix Pubkey, // Needs initalising pub offer_token: &'ix Pubkey, pub auction_custody_token: &'ix Pubkey, pub usdc: &'ix Pubkey, @@ -101,11 +109,24 @@ pub struct VaaMessageBodyHeader { } impl VaaMessageBodyHeader { - pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32]) -> Self { - Self { consistency_level, vaa_time, nonce: 0, sequence, emitter_chain, emitter_address } + pub fn new( + consistency_level: u8, + vaa_time: u32, + sequence: u64, + emitter_chain: u16, + emitter_address: [u8; 32], + ) -> Self { + Self { + consistency_level, + vaa_time, + nonce: 0, + sequence, + emitter_chain, + emitter_address, + } } - /// This function creates both the message body and the payload. + /// This function creates both the message body and the payload. /// This is all done here just because it's (supposedly?) cheaper in the solana vm. pub fn message_body(&self, fast_market_order: &FastMarketOrderState) -> Vec { let mut message_body = vec![]; @@ -128,16 +149,17 @@ impl VaaMessageBodyHeader { payload.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); payload.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); if fast_market_order.redeemer_message_length > 0 { - payload.extend_from_slice(&fast_market_order.redeemer_message[..fast_market_order.redeemer_message_length as usize]); + payload.extend_from_slice( + &fast_market_order.redeemer_message + [..fast_market_order.redeemer_message_length as usize], + ); } message_body.extend_from_slice(&payload); message_body } pub fn message_hash(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { - keccak::hashv(&[ - self.message_body(fast_market_order).as_ref() - ]) + keccak::hashv(&[self.message_body(fast_market_order).as_ref()]) } pub fn digest(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { @@ -157,10 +179,13 @@ impl VaaMessageBodyHeader { } } -pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { +pub fn place_initial_offer_cctp_shim( + accounts: &[AccountInfo], + data: &PlaceInitialOfferCctpShimData, +) -> Result<()> { // Check account owners let program_id = &crate::ID; // Your program ID - + // Check all accounts are valid if accounts.len() < 11 { return Err(ErrorCode::AccountNotEnoughKeys.into()); @@ -187,26 +212,32 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti let offer_token = &accounts[8]; let auction_custody_token = &accounts[9]; let usdc = &accounts[10]; - - let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; + let fast_market_order_zero_copy = + FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; // Check pda of the transfer authority is valid let transfer_authority_seeds = [ TRANSFER_AUTHORITY_SEED_PREFIX, auction_key.as_ref(), - &offer_price.to_be_bytes() + &offer_price.to_be_bytes(), ]; - let (transfer_authority_pda, transfer_authority_bump) = Pubkey::find_program_address(&transfer_authority_seeds, &PROGRAM_ID); + let (transfer_authority_pda, transfer_authority_bump) = + Pubkey::find_program_address(&transfer_authority_seeds, &PROGRAM_ID); if transfer_authority_pda != transfer_authority.key() { msg!("Transfer authority pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((transfer_authority_pda, transfer_authority.key()))); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys((transfer_authority_pda, transfer_authority.key())) + }); } // Check custodian owner if custodian.owner != program_id { - msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian.owner); + msg!( + "Custodian owner is invalid: expected {}, got {}", + program_id, + custodian.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("custodian")); } @@ -219,56 +250,70 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti } // Check auction_config owner if auction_config.owner != program_id { - msg!("Auction config owner is invalid: expected {}, got {}", program_id, auction_config.owner); + msg!( + "Auction config owner is invalid: expected {}, got {}", + program_id, + auction_config.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("auction_config")); } // Check auction config id is correct corresponding to the custodian - let auction_config_account = AuctionConfig::try_deserialize(&mut &auction_config.data.borrow()[..])?; + let auction_config_account = + AuctionConfig::try_deserialize(&mut &auction_config.data.borrow()[..])?; if auction_config_account.id != checked_custodian.auction_config_id { msg!("Auction config id is invalid"); return Err(ErrorCode::ConstraintRaw.into()) .map_err(|e: Error| e.with_account_name("auction_config")); } - + // Check usdc mint if usdc.key() != common::USDC_MINT { msg!("Usdc mint is invalid"); return Err(FallbackError::InvalidMint.into()); } - + // Check from_endpoint owner if from_endpoint.owner != program_id { - msg!("From endpoint owner is invalid: expected {}, got {}", program_id, from_endpoint.owner); + msg!( + "From endpoint owner is invalid: expected {}, got {}", + program_id, + from_endpoint.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("from_endpoint")); } - + // Deserialise the from_endpoint account - let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; - + let from_endpoint_account = + RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; + // Check to_endpoint owner if to_endpoint.owner != program_id { - msg!("To endpoint owner is invalid: expected {}, got {}", program_id, to_endpoint.owner); + msg!( + "To endpoint owner is invalid: expected {}, got {}", + program_id, + to_endpoint.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("to_endpoint")); } - + // Deserialise the to_endpoint account let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; - + // Check that the from and to endpoints are different if from_endpoint_account.chain == to_endpoint_account.chain { return Err(MatchingEngineError::SameEndpoint.into()); } - + // Check that the to endpoint protocol is cctp or local match to_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), _ => return Err(MatchingEngineError::InvalidEndpoint.into()), } - + // Check that the from endpoint protocol is cctp or local match from_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), @@ -296,21 +341,37 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti return Err(MatchingEngineError::OfferPriceTooHigh.into()); } } - + // Create the vaa_message struct to get the digest - let vaa_message = VaaMessageBodyHeader::new(consistency_level, vaa_time, sequence, from_endpoint_account.chain, from_endpoint_account.address); + let vaa_message = VaaMessageBodyHeader::new( + consistency_level, + vaa_time, + sequence, + from_endpoint_account.chain, + from_endpoint_account.address, + ); let vaa_message_digest = vaa_message.digest(&fast_market_order_zero_copy); // Begin of initialisation of auction custody token account // ------------------------------------------------------------------------------------------------ let auction_custody_token_space = spl_token::state::Account::LEN; - let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address(&[crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_key.as_ref()], &program_id); + let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address( + &[ + crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_key.as_ref(), + ], + &program_id, + ); if auction_custody_token_pda != auction_custody_token.key() { - msg!("Auction custody token pda is invalid. Passed account: {}, expected: {}", auction_custody_token.key(), auction_custody_token_pda); + msg!( + "Auction custody token pda is invalid. Passed account: {}, expected: {}", + auction_custody_token.key(), + auction_custody_token_pda + ); return Err(FallbackError::InvalidPda.into()); } - + let auction_custody_token_seeds = [ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_key.as_ref(), @@ -332,25 +393,19 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti &auction_custody_token_pda, &usdc.key(), &auction_account.key(), - ).unwrap(); - - solana_program::program::invoke( - &init_token_account_ix, - accounts, - ).unwrap(); - + ) + .unwrap(); + + solana_program::program::invoke(&init_token_account_ix, accounts).unwrap(); + // ------------------------------------------------------------------------------------------------ // End of initialisation of auction custody token account - // Begin of initialisation of auction account // ------------------------------------------------------------------------------------------------ let auction_space = 8 + Auction::INIT_SPACE; let (pda, bump) = Pubkey::find_program_address( - &[ - Auction::SEED_PREFIX, - vaa_message_digest.as_ref(), - ], + &[Auction::SEED_PREFIX, vaa_message_digest.as_ref()], &program_id, ); @@ -358,11 +413,7 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti msg!("Auction pda is invalid"); return Err(FallbackError::InvalidPda.into()); } - let auction_seeds = [ - Auction::SEED_PREFIX, - vaa_message_digest.as_ref(), - &[bump], - ]; + let auction_seeds = [Auction::SEED_PREFIX, vaa_message_digest.as_ref(), &[bump]]; let auction_signer_seeds = &[&auction_seeds[..]]; create_account_reliably( &signer.key(), @@ -374,21 +425,28 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti auction_signer_seeds, )?; // Borrow the account data mutably - let mut data = auction_account.try_borrow_mut_data().map_err(|_| FallbackError::AccountNotWritable)?; + let mut data = auction_account + .try_borrow_mut_data() + .map_err(|_| FallbackError::AccountNotWritable)?; // Write the discriminator to the first 8 bytes let discriminator = Auction::discriminator(); data[0..8].copy_from_slice(&discriminator); - let security_deposit = fast_market_order_zero_copy.max_fee - .saturating_add(crate::utils::auction::compute_notional_security_deposit( - &auction_config_account.parameters, - fast_market_order_zero_copy.amount_in, - )); + let security_deposit = fast_market_order_zero_copy.max_fee.saturating_add( + crate::utils::auction::compute_notional_security_deposit( + &auction_config_account.parameters, + fast_market_order_zero_copy.amount_in, + ), + ); let auction_to_write = Auction { bump, - vaa_hash: vaa_message.digest(&fast_market_order_zero_copy).as_ref().try_into().unwrap(), + vaa_hash: vaa_message + .digest(&fast_market_order_zero_copy) + .as_ref() + .try_into() + .unwrap(), vaa_timestamp: vaa_message.vaa_time(), target_protocol: to_endpoint_account.protocol, status: AuctionStatus::Active, @@ -410,11 +468,13 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti .into(), }; // Write the auction struct to the account - let auction_bytes = auction_to_write.try_to_vec().map_err(|_| FallbackError::BorshDeserializationError)?; + let auction_bytes = auction_to_write + .try_to_vec() + .map_err(|_| FallbackError::BorshDeserializationError)?; data[8..8 + auction_bytes.len()].copy_from_slice(&auction_bytes); // ------------------------------------------------------------------------------------------------ // End of initialisation of auction account - + // Start of token transfer from offer token to auction custody token // ------------------------------------------------------------------------------------------------ @@ -424,16 +484,23 @@ pub fn place_initial_offer_cctp_shim(accounts: &[AccountInfo], data: &PlaceIniti &auction_custody_token.key(), &transfer_authority.key(), &[], // Apparently this is only for multi-sig accounts - fast_market_order_zero_copy.amount_in + fast_market_order_zero_copy + .amount_in .checked_add(security_deposit) - .ok_or_else(|| MatchingEngineError::U64Overflow)? - ).unwrap(); - invoke_signed_unchecked(&transfer_ix, accounts, &[&[ - TRANSFER_AUTHORITY_SEED_PREFIX, - auction_key.as_ref(), - &offer_price.to_be_bytes(), - &[transfer_authority_bump], - ]]).map_err(|_| FallbackError::TokenTransferFailed)?; + .ok_or_else(|| MatchingEngineError::U64Overflow)?, + ) + .unwrap(); + invoke_signed_unchecked( + &transfer_ix, + accounts, + &[&[ + TRANSFER_AUTHORITY_SEED_PREFIX, + auction_key.as_ref(), + &offer_price.to_be_bytes(), + &[transfer_authority_bump], + ]], + ) + .map_err(|_| FallbackError::TokenTransferFailed)?; // ------------------------------------------------------------------------------------------------ // End of token transfer from offer token to auction custody token Ok(()) @@ -458,15 +525,14 @@ mod tests { 0, [0_u8; 512], [0_u8; 32], - [0_u8; 32], + 0, + 0, 0, 0, 0, [0_u8; 32], - ); let bytes = bytemuck::bytes_of(&test_fast_market_order); assert!(bytes.len() == std::mem::size_of::()); } - } diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 6c5a86b3c..f19f847a9 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -1,23 +1,27 @@ use std::io::Cursor; +use super::create_account::create_account_reliably; +use super::FallbackMatchingEngineInstruction; +use crate::state::PreparedOrderResponseInfo; +use crate::state::PreparedOrderResponseSeeds; +use crate::state::{ + Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, PreparedOrderResponse, + RouterEndpoint, +}; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use solana_program::program_pack::Pack; +use common::messages::raw::LiquidityLayerDepositMessage; use common::messages::raw::SlowOrderResponse; +use common::wormhole_cctp_solana::cctp::message_transmitter_program; use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; -use common::wormhole_cctp_solana::utils::CctpMessage; -use solana_program::program::invoke_signed_unchecked; -use super::create_account::create_account_reliably; use solana_program::instruction::Instruction; -use crate::state::PreparedOrderResponseInfo; -use crate::state::PreparedOrderResponseSeeds; -use crate::state::{Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, PreparedOrderResponse, RouterEndpoint}; -use common::wormhole_cctp_solana::cctp::message_transmitter_program; +use solana_program::keccak; +use solana_program::program::invoke_signed_unchecked; +use solana_program::program_pack::Pack; use super::errors::FallbackError; use crate::error::MatchingEngineError; - #[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] pub struct PrepareOrderResponseCctpShimData { pub encoded_cctp_message: Vec, @@ -27,32 +31,59 @@ pub struct PrepareOrderResponseCctpShimData { #[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] pub struct FinalizedVaaMessage { - pub vaa_sequence: u64, - pub vaa_timestamp: u32, - pub vaa_emitter_chain: u16, - pub vaa_emitter_address: [u8; 32], - pub base_fee: u64, - pub deposit_message: DepositMessage, + pub base_fee: u64, // Can also get from deposit payload + pub vaa_payload: Vec, // Can get a lot of this info from the cctp message + pub deposit_payload: Vec, // Probably dont need this since its in the vaa payload (at the end) pub guardian_set_bump: u8, } -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] -pub struct DepositMessage { - pub token_address: [u8; 32], - pub amount: [u8; 32], // little endian - pub source_cctp_domain: u32, - pub destination_cctp_domain: u32, - pub cctp_nonce: u64, - pub burn_source: [u8; 32], - pub mint_recipient: [u8; 32], - pub digest: [u8; 32], - pub payload_len: u16, - pub payload: Vec, +impl FinalizedVaaMessage { + pub fn digest( + &self, + sequence: u64, + timestamp: u32, + emitter_chain: u16, + emitter_address: [u8; 32], + nonce: u32, + consistency_level: u8, + ) -> [u8; 32] { + let message_hash = keccak::hashv(&[ + timestamp.to_be_bytes().as_ref(), + nonce.to_be_bytes().as_ref(), + emitter_chain.to_be_bytes().as_ref(), + &emitter_address, + &sequence.to_be_bytes(), + &[consistency_level], + self.vaa_payload.as_ref(), + ]); + // Digest is the hash of the message + keccak::hashv(&[message_hash.as_ref()]) + .as_ref() + .try_into() + .unwrap() + } + + pub fn get_slow_order_response<'a>(&'a self) -> SlowOrderResponse<'a> { + let liquidity_layer_message: LiquidityLayerDepositMessage<'a> = + LiquidityLayerDepositMessage::parse(&self.deposit_payload) + .expect("Cannot get slow order response from deposit payload"); + let slow_order_response: SlowOrderResponse<'a> = + liquidity_layer_message.to_slow_order_response_unchecked(); + slow_order_response + } } impl PrepareOrderResponseCctpShimData { - pub fn new(encoded_cctp_message: Vec, cctp_attestation: Vec, finalized_vaa_message: FinalizedVaaMessage) -> Self { - Self { encoded_cctp_message, cctp_attestation, finalized_vaa_message } + pub fn new( + encoded_cctp_message: Vec, + cctp_attestation: Vec, + finalized_vaa_message: FinalizedVaaMessage, + ) -> Self { + Self { + encoded_cctp_message, + cctp_attestation, + finalized_vaa_message, + } } pub fn from_bytes(data: &[u8]) -> Option { Self::try_from_slice(data).ok() @@ -66,54 +97,57 @@ impl PrepareOrderResponseCctpShimData { encoded_message.extend_from_slice(&self.encoded_cctp_message); let mut cctp_attestation = Vec::with_capacity(self.cctp_attestation.len()); cctp_attestation.extend_from_slice(&self.cctp_attestation); - ReceiveMessageArgs { encoded_message, attestation: cctp_attestation } + ReceiveMessageArgs { + encoded_message, + attestation: cctp_attestation, + } } } pub struct PrepareOrderResponseCctpShimAccounts<'ix> { - pub signer: &'ix Pubkey, - pub custodian: &'ix Pubkey, - pub fast_market_order: &'ix Pubkey, - pub from_endpoint: &'ix Pubkey, - pub to_endpoint: &'ix Pubkey, - pub prepared_order_response: &'ix Pubkey, - pub prepared_custody_token: &'ix Pubkey, - pub base_fee_token: &'ix Pubkey, - pub usdc: &'ix Pubkey, - pub cctp_mint_recipient: &'ix Pubkey, - pub cctp_message_transmitter_authority: &'ix Pubkey, - pub cctp_message_transmitter_config: &'ix Pubkey, - pub cctp_used_nonces: &'ix Pubkey, - pub cctp_message_transmitter_event_authority: &'ix Pubkey, - pub cctp_token_messenger: &'ix Pubkey, - pub cctp_remote_token_messenger: &'ix Pubkey, - pub cctp_token_minter: &'ix Pubkey, - pub cctp_local_token: &'ix Pubkey, - pub cctp_token_pair: &'ix Pubkey, - pub cctp_token_messenger_minter_custody_token: &'ix Pubkey, - pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, - pub cctp_token_messenger_minter_program: &'ix Pubkey, - pub cctp_message_transmitter_program: &'ix Pubkey, - pub guardian_set: &'ix Pubkey, - pub guardian_set_signatures: &'ix Pubkey, - pub verify_shim_program: &'ix Pubkey, - pub token_program: &'ix Pubkey, - pub system_program: &'ix Pubkey, + pub signer: &'ix Pubkey, // 0 + pub custodian: &'ix Pubkey, // 1 + pub fast_market_order: &'ix Pubkey, // 2 + pub from_endpoint: &'ix Pubkey, // 3 + pub to_endpoint: &'ix Pubkey, // 4 + pub prepared_order_response: &'ix Pubkey, // 5 + pub prepared_custody_token: &'ix Pubkey, // 6 + pub base_fee_token: &'ix Pubkey, // 7 + pub usdc: &'ix Pubkey, // 8 + pub cctp_mint_recipient: &'ix Pubkey, // 9 + pub cctp_message_transmitter_authority: &'ix Pubkey, // 10 + pub cctp_message_transmitter_config: &'ix Pubkey, // 11 + pub cctp_used_nonces: &'ix Pubkey, // 12 + pub cctp_message_transmitter_event_authority: &'ix Pubkey, // 13 + pub cctp_token_messenger: &'ix Pubkey, // 14 + pub cctp_remote_token_messenger: &'ix Pubkey, // 15 + pub cctp_token_minter: &'ix Pubkey, // 16 + pub cctp_local_token: &'ix Pubkey, // 17 + pub cctp_token_pair: &'ix Pubkey, // 18 + pub cctp_token_messenger_minter_custody_token: &'ix Pubkey, // 19 + pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, // 20 + pub cctp_token_messenger_minter_program: &'ix Pubkey, // 21 + pub cctp_message_transmitter_program: &'ix Pubkey, // 22 + pub guardian_set: &'ix Pubkey, // 23 + pub guardian_set_signatures: &'ix Pubkey, // 24 + pub verify_shim_program: &'ix Pubkey, // 25 + pub token_program: &'ix Pubkey, // 26 + pub system_program: &'ix Pubkey, // 27 } impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ - AccountMeta::new(*self.signer, false), + AccountMeta::new(*self.signer, true), AccountMeta::new_readonly(*self.custodian, false), AccountMeta::new_readonly(*self.fast_market_order, false), AccountMeta::new_readonly(*self.from_endpoint, false), AccountMeta::new_readonly(*self.to_endpoint, false), - AccountMeta::new_readonly(*self.prepared_order_response, false), - AccountMeta::new_readonly(*self.prepared_custody_token, false), + AccountMeta::new(*self.prepared_order_response, false), + AccountMeta::new(*self.prepared_custody_token, false), AccountMeta::new_readonly(*self.base_fee_token, false), AccountMeta::new_readonly(*self.usdc, false), - AccountMeta::new_readonly(*self.cctp_mint_recipient, false), + AccountMeta::new(*self.cctp_mint_recipient, false), AccountMeta::new_readonly(*self.cctp_message_transmitter_authority, false), AccountMeta::new_readonly(*self.cctp_message_transmitter_config, false), AccountMeta::new(*self.cctp_used_nonces, false), @@ -136,6 +170,7 @@ impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { } } +// TODO: Also close the fast market order account since it is no longer needed pub struct PrepareOrderResponseCctpShim<'ix> { pub program_id: &'ix Pubkey, pub accounts: PrepareOrderResponseCctpShimAccounts<'ix>, @@ -143,20 +178,25 @@ pub struct PrepareOrderResponseCctpShim<'ix> { } impl<'ix> PrepareOrderResponseCctpShim<'ix> { - pub fn instruction(&self) -> Instruction { + pub fn instruction(self) -> Instruction { Instruction { program_id: *self.program_id, accounts: self.accounts.to_account_metas(), - data: self.data.to_bytes(), + data: FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(self.data) + .to_vec(), } } } -pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData) -> Result<()> { +pub fn prepare_order_response_cctp_shim( + accounts: &[AccountInfo], + data: PrepareOrderResponseCctpShimData, +) -> Result<()> { let program_id = &crate::ID; if accounts.len() < 27 { return Err(ErrorCode::AccountNotEnoughKeys.into()); } + let signer = &accounts[0]; let custodian = &accounts[1]; let fast_market_order = &accounts[2]; @@ -182,60 +222,103 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO let cctp_message_transmitter_program = &accounts[22]; let guardian_set = &accounts[23]; let guardian_set_signatures = &accounts[24]; - let _verify_shim_program = &accounts[25]; + let _verify_shim_program = &accounts[25]; let token_program = &accounts[26]; let system_program = &accounts[27]; - let receive_message_args = data.to_receive_message_args(); let finalized_vaa_message = data.finalized_vaa_message; - let deposit_message = finalized_vaa_message.deposit_message; - + // Load accounts + let fast_market_order_zero_copy = + FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + // Create pdas for addresses that need to be created + // Check the prepared order response account is valid + // TODO: Pass the digest so it isn't recomputed + let fast_market_order_digest = fast_market_order_zero_copy.digest(); + // Construct the finalised vaa message digest data + let finalized_vaa_message_digest = { + let finalised_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; + let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence - 1; + let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; + let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; + let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; + let finalised_vaa_consistency_level = fast_market_order_zero_copy.vaa_consistency_level; + msg!("Finalised vaa sequence: {:?}", finalised_vaa_sequence); + msg!("Finalised vaa nonce: {:?}", finalised_vaa_nonce); + &finalized_vaa_message.digest( + finalised_vaa_sequence, + finalised_vaa_timestamp, + finalised_vaa_emitter_chain, + finalised_vaa_emitter_address, + finalised_vaa_nonce, + finalised_vaa_consistency_level, + ) + }; + + msg!( + "Finalized VAA message digest: {:?}", + finalized_vaa_message_digest + ); + // Check that fast market order is owned by the program if fast_market_order.owner != program_id { - msg!("Fast market order owner is invalid: expected {}, got {}", program_id, fast_market_order.owner); + msg!( + "Fast market order owner is invalid: expected {}, got {}", + program_id, + fast_market_order.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("fast_market_order")); } - - // Load accounts - let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; - // Load from cctp message. - let cctp_message = CctpMessage::parse(&data.encoded_cctp_message).map_err(|_| FallbackError::InvalidCctpMessage)?; - let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; + let checked_custodian = + Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; // Deserialise the to_endpoint account - let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; + + // TODO: Scope this to do checks and deallocate stack + let to_endpoint_account = + RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..]).map(Box::new)?; // Deserialise the from_endpoint account - let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; + let from_endpoint_account = + RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..]).map(Box::new)?; let guardian_set_bump = finalized_vaa_message.guardian_set_bump; // Check loaded vaa is deposit message - let slow_order_response = SlowOrderResponse::parse(&deposit_message.payload).map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; + // TODO: Fix errors + let liquidity_layer_message = + LiquidityLayerDepositMessage::parse(&finalized_vaa_message.deposit_payload) + .map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; + let slow_order_response = liquidity_layer_message + .slow_order_response() + .ok_or_else(|| MatchingEngineError::InvalidDepositPayloadId)?; - // Create pdas for addresses that need to be created - // Check the prepared order response account is valid let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_zero_copy.digest + &fast_market_order_digest, ]; - let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, program_id); + let (prepared_order_response_pda, prepared_order_response_bump) = + Pubkey::find_program_address(&prepared_order_response_seeds, program_id); let prepared_custody_token_seeds = [ crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, prepared_order_response_pda.as_ref(), ]; - let (prepared_custody_token_pda, prepared_custody_token_bump) = Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); + let (prepared_custody_token_pda, prepared_custody_token_bump) = + Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); // Check custodian account if custodian.owner != program_id { - msg!("Custodian owner is invalid: expected {}, got {}", program_id, custodian.owner); + msg!( + "Custodian owner is invalid: expected {}, got {}", + program_id, + custodian.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("custodian")); } - + if checked_custodian.paused { msg!("Custodian is paused"); return Err(ErrorCode::ConstraintRaw.into()) @@ -247,33 +330,40 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO msg!("Usdc mint is invalid"); return Err(FallbackError::InvalidMint.into()); } - + // Check from_endpoint owner if from_endpoint.owner != program_id { - msg!("From endpoint owner is invalid: expected {}, got {}", program_id, from_endpoint.owner); + msg!( + "From endpoint owner is invalid: expected {}, got {}", + program_id, + from_endpoint.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("from_endpoint")); } - + // Check to_endpoint owner if to_endpoint.owner != program_id { - msg!("To endpoint owner is invalid: expected {}, got {}", program_id, to_endpoint.owner); + msg!( + "To endpoint owner is invalid: expected {}, got {}", + program_id, + to_endpoint.owner + ); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("to_endpoint")); } - - + // Check that the from and to endpoints are different if from_endpoint_account.chain == to_endpoint_account.chain { return Err(MatchingEngineError::SameEndpoint.into()); } - + // Check that the to endpoint protocol is cctp or local match to_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), _ => return Err(MatchingEngineError::InvalidEndpoint.into()), } - + // Check that the from endpoint protocol is cctp or local match from_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), @@ -282,74 +372,22 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO // Check that to endpoint chain is equal to the fast_market_order target_chain if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { - msg!("To endpoint chain is not equal to the fast_market_order target_chain"); + msg!("To endpoint chain is not equal to the fast_market_order target_chain. Expected {}, got {}", fast_market_order_zero_copy.target_chain, to_endpoint_account.chain); return Err(MatchingEngineError::InvalidTargetRouter.into()); } if prepared_order_response_pda != prepared_order_response.key() { msg!("Prepared order response pda is invalid"); - return Err(FallbackError::InvalidPda.into()) - .map_err(|e: Error| e.with_pubkeys((prepared_order_response_pda, prepared_order_response.key()))); + return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| { + e.with_pubkeys((prepared_order_response_pda, prepared_order_response.key())) + }); } if prepared_custody_token_pda != prepared_custody_token.key() { msg!("Prepared custody token pda is invalid"); - return Err(FallbackError::InvalidPda.into()) - .map_err(|e: Error| e.with_pubkeys((prepared_custody_token_pda, prepared_custody_token.key()))); - } - - // Check vaa emitter chain matches fast market order emitter chain - if fast_market_order_zero_copy.vaa_emitter_chain != finalized_vaa_message.vaa_emitter_chain { - msg!("Vaa emitter chain does not match fast market order emitter chain"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - // TODO: Figure out how to find emitter address to check against - - // Check vaa emitter address matches fast market order emitter address - if fast_market_order_zero_copy.vaa_emitter_address != finalized_vaa_message.vaa_emitter_address { - msg!("Vaa emitter address does not match fast market order emitter address"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - // TODO: Figure out how to check the sequence number - if fast_market_order_zero_copy.vaa_sequence != finalized_vaa_message.vaa_sequence.saturating_add(1) { - msg!("Vaa sequence must be exactly 1 greater than fast market order sequence"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - // TODO: Figure out how to check the timestamp - if fast_market_order_zero_copy.vaa_timestamp != finalized_vaa_message.vaa_timestamp { - msg!("Vaa timestamp does not match fast market order timestamp"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - // TODO: Make checks against cctp message - if cctp_message.sender() != fast_market_order_zero_copy.sender { - msg!("Cctp message sender does not match fast market order sender"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - if cctp_message.destination_domain() != deposit_message.destination_cctp_domain { - msg!("Cctp message destination domain does not match deposit message destination domain"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - if cctp_message.nonce() != deposit_message.cctp_nonce { - msg!("Cctp message nonce does not match deposit message nonce"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } - - if cctp_message.source_domain() != deposit_message.source_cctp_domain { - msg!("Cctp message source domain does not match deposit message source domain"); - return Err(MatchingEngineError::VaaMismatch.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); + return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| { + e.with_pubkeys((prepared_custody_token_pda, prepared_custody_token.key())) + }); } // Check the base token fee key is not equal to the prepared custody token key @@ -383,9 +421,11 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO // ------------------------------------------------------------------------------------------------ let verify_hash_data = { let mut data = vec![]; - data.extend_from_slice(&wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR); + data.extend_from_slice( + &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, + ); data.push(guardian_set_bump); - data.extend_from_slice(&fast_market_order_zero_copy.digest); + data.extend_from_slice(finalized_vaa_message_digest); data }; @@ -397,10 +437,7 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO ], data: verify_hash_data, }; - invoke_signed_unchecked(&verify_shim_ix, &[ - guardian_set.to_account_info(), - guardian_set_signatures.to_account_info(), - ], &[])?; + invoke_signed_unchecked(&verify_shim_ix, &accounts, &[])?; // End verify deposit message vaa shim // ------------------------------------------------------------------------------------------------ @@ -412,14 +449,16 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO // * settle_auction_active_cctp // * settle_auction_complete // * settle_auction_none + let create_prepared_order_respone_seeds = [ PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_zero_copy.digest, + &fast_market_order_digest, &[prepared_order_response_bump], ]; let prepared_order_response_signer_seeds = &[&create_prepared_order_respone_seeds[..]]; - let prepared_order_response_account_space = PreparedOrderResponse::compute_size(fast_market_order_zero_copy.redeemer_message_length.into()); - + let prepared_order_response_account_space = PreparedOrderResponse::compute_size( + fast_market_order_zero_copy.redeemer_message_length.into(), + ); create_account_reliably( &signer.key(), &prepared_order_response.key(), @@ -429,11 +468,10 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO program_id, prepared_order_response_signer_seeds, )?; - // Write the prepared order response account data ... let prepared_order_response_account_to_write = PreparedOrderResponse { seeds: PreparedOrderResponseSeeds { - fast_vaa_hash: fast_market_order_zero_copy.digest, + fast_vaa_hash: fast_market_order_digest, bump: prepared_order_response_bump, }, info: PreparedOrderResponseInfo { @@ -448,13 +486,18 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO init_auction_fee: fast_market_order_zero_copy.init_auction_fee, }, to_endpoint: to_endpoint_account.info, - redeemer_message: fast_market_order_zero_copy.redeemer_message[..fast_market_order_zero_copy.redeemer_message_length as usize].to_vec(), + redeemer_message: fast_market_order_zero_copy.redeemer_message + [..fast_market_order_zero_copy.redeemer_message_length as usize] + .to_vec(), }; // Use cursor in order to write the prepared order response account data - let prepared_order_response_data: &mut [u8] = &mut prepared_order_response.try_borrow_mut_data().map_err(|_| FallbackError::AccountNotWritable)?; + let prepared_order_response_data: &mut [u8] = &mut prepared_order_response + .try_borrow_mut_data() + .map_err(|_| FallbackError::AccountNotWritable)?; let mut cursor = Cursor::new(prepared_order_response_data); - prepared_order_response_account_to_write.try_serialize(&mut cursor).map_err(|_| FallbackError::BorshDeserializationError)?; - + prepared_order_response_account_to_write + .try_serialize(&mut cursor) + .map_err(|_| FallbackError::BorshDeserializationError)?; // End create prepared order response account // ------------------------------------------------------------------------------------------------ @@ -474,21 +517,16 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO prepared_custody_token.lamports(), prepared_custody_token_account_space, accounts, - program_id, + &spl_token::ID, prepared_custody_token_signer_seeds, )?; let init_token_account_ix = spl_token::instruction::initialize_account3( &spl_token::ID, &prepared_custody_token_pda, &usdc.key(), - &prepared_custody_token.key(), - ).unwrap(); - - solana_program::program::invoke( - &init_token_account_ix, - accounts, - ).unwrap(); - + &prepared_order_response_pda, + )?; + solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[])?; // End create prepared custody token account // ------------------------------------------------------------------------------------------------ @@ -502,9 +540,11 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO message_transmitter_authority: cctp_message_transmitter_authority.to_account_info(), message_transmitter_config: cctp_message_transmitter_config.to_account_info(), used_nonces: cctp_used_nonces.to_account_info(), - token_messenger_minter_program: cctp_token_messenger_minter_program.to_account_info(), + token_messenger_minter_program: cctp_token_messenger_minter_program + .to_account_info(), system_program: system_program.to_account_info(), - message_transmitter_event_authority: cctp_message_transmitter_event_authority.to_account_info(), + message_transmitter_event_authority: cctp_message_transmitter_event_authority + .to_account_info(), message_transmitter_program: cctp_message_transmitter_program.to_account_info(), token_messenger: cctp_token_messenger.to_account_info(), remote_token_messenger: cctp_remote_token_messenger.to_account_info(), @@ -514,28 +554,27 @@ pub fn prepare_order_response_cctp_shim(accounts: &[AccountInfo], data: PrepareO mint_recipient: cctp_mint_recipient.to_account_info(), custody_token: cctp_token_messenger_minter_custody_token.to_account_info(), token_program: token_program.to_account_info(), - token_messenger_minter_event_authority: cctp_token_messenger_minter_event_authority.to_account_info(), + token_messenger_minter_event_authority: cctp_token_messenger_minter_event_authority + .to_account_info(), }, &[Custodian::SIGNER_SEEDS], ), receive_message_args, )?; - + // Finally transfer minted via CCTP to prepared custody token. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, &cctp_mint_recipient.key(), &prepared_custody_token.key(), - &cctp_message_transmitter_authority.key(), + &custodian.key(), &[], // Apparently this is only for multi-sig accounts fast_market_order_zero_copy.amount_in, - ).unwrap(); + ) + .unwrap(); - invoke_signed_unchecked(&transfer_ix, accounts, &[ - Custodian::SIGNER_SEEDS, - ]).map_err(|_| FallbackError::TokenTransferFailed)?; + invoke_signed_unchecked(&transfer_ix, accounts, &[Custodian::SIGNER_SEEDS]) + .map_err(|_| FallbackError::TokenTransferFailed)?; Ok(()) } - - diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 0a9761da2..036159b08 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -1,23 +1,27 @@ - -use crate::ID; -use anchor_lang::prelude::*; -use wormhole_svm_definitions::make_anchor_discriminator; -use super::place_initial_offer::PlaceInitialOfferCctpShimData; -use super::place_initial_offer::place_initial_offer_cctp_shim; -use super::execute_order::handle_execute_order_shim; -use super::initialise_fast_market_order::{initialise_fast_market_order, InitialiseFastMarketOrderData}; use super::close_fast_market_order::close_fast_market_order; +use super::execute_order::handle_execute_order_shim; +use super::initialise_fast_market_order::{ + initialise_fast_market_order, InitialiseFastMarketOrderData, +}; +use super::place_initial_offer::place_initial_offer_cctp_shim; +use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::prepare_order_response::prepare_order_response_cctp_shim; use super::prepare_order_response::PrepareOrderResponseCctpShimData; - - +use crate::ID; +use anchor_lang::prelude::*; +use wormhole_svm_definitions::make_anchor_discriminator; impl<'ix> FallbackMatchingEngineInstruction<'ix> { - pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); - pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:execute_order_cctp_shim"); - pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:initialise_fast_market_order"); - pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:close_fast_market_order"); - pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); + pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); + pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:execute_order_cctp_shim"); + pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:initialise_fast_market_order"); + pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:close_fast_market_order"); + pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); } pub enum FallbackMatchingEngineInstruction<'ix> { @@ -28,7 +32,11 @@ pub enum FallbackMatchingEngineInstruction<'ix> { PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } -pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> Result<()> { if program_id != &ID { return Err(ErrorCode::InvalidProgramId.into()); } @@ -37,13 +45,13 @@ pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instru match instruction { FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, &data) - }, + } FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { handle_execute_order_shim(accounts) - }, + } FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(data) => { initialise_fast_market_order(accounts, &data) - }, + } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { close_fast_market_order(accounts) } @@ -61,17 +69,24 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { match instruction_data[..8].try_into().unwrap() { FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { - Some(Self::PlaceInitialOfferCctpShim(&PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap())) - }, + Some(Self::PlaceInitialOfferCctpShim( + &PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + )) + } FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { Some(Self::ExecuteOrderCctpShim) - }, - FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => { - Some(Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..]))) - }, + } + FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => Some( + Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..])), + ), FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) - }, + } + FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { + Some(Self::PrepareOrderResponseCctpShim( + PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + )) + } _ => None, } } @@ -89,20 +104,23 @@ impl FallbackMatchingEngineInstruction<'_> { let mut out = Vec::with_capacity(total_capacity); // Add the selector - out.extend_from_slice(&FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR, + ); // Extend the vector with the data slice out.extend_from_slice(data_slice); out - }, + } Self::ExecuteOrderCctpShim => { let total_capacity = 8; // 8 for the selector (no data) let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice(&FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR); - + out.extend_from_slice( + &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, + ); out } @@ -112,20 +130,24 @@ impl FallbackMatchingEngineInstruction<'_> { let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice(&FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR, + ); out.extend_from_slice(data_slice); out - }, + } Self::CloseFastMarketOrder => { let total_capacity = 8; // 8 for the selector (no data) let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice(&FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR, + ); out - }, + } Self::PrepareOrderResponseCctpShim(data) => { let data_slice = data.to_bytes(); @@ -133,11 +155,13 @@ impl FallbackMatchingEngineInstruction<'_> { let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice(&FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, + ); out.extend_from_slice(&data_slice); out } } } -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 72fa5404c..93d632ae9 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -257,8 +257,8 @@ pub mod matching_engine { processor::place_initial_offer_cctp(ctx, offer_price) } - /// This instruction is used to create a new auction given a valid `VaaShim`. - /// This instruction should act in the exact same way as `place_initial_offer_cctp` except that + /// This instruction is used to create a new auction given a valid `VaaShim`. + /// This instruction should act in the exact same way as `place_initial_offer_cctp` except that /// it will check the digest of the vaa directly using a cpi call to the verify shim program. // pub fn place_initial_offer_cctp_shim(ctx: Context, offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage) -> Result<()> { // processor::place_initial_offer_cctp_shim(ctx, offer_price, guardian_set_bump, vaa_message) @@ -486,10 +486,13 @@ pub mod matching_engine { } /// Non anchor function for placing an initial offer using the VAA shim. - pub fn fallback_process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> Result<()> { + pub fn fallback_process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], + ) -> Result<()> { fallback::process_instruction(program_id, accounts, instruction_data) } - } #[derive(Accounts)] diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs index e9832bcf2..44f301b0f 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs @@ -205,4 +205,4 @@ pub fn place_initial_offer_cctp( .checked_add(security_deposit) .ok_or_else(|| MatchingEngineError::U64Overflow)?, ) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index d7c73a4f8..34b83397e 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -8,7 +8,6 @@ use anchor_spl::token; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use solana_program::keccak; - #[derive(Accounts)] #[instruction(offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage)] pub struct PlaceInitialOfferCctpShim<'info> { @@ -94,7 +93,7 @@ pub struct PlaceInitialOfferCctpShim<'info> { auction_custody_token: Box>, usdc: Usdc<'info>, - + #[account(constraint = { require_eq!( verify_vaa_shim_program.key(), @@ -109,14 +108,31 @@ pub struct PlaceInitialOfferCctpShim<'info> { token_program: Program<'info, token::Token>, } -// TODO: Change this to be PlaceInitialOfferArgs and go from there ... +// TODO: Change this to be PlaceInitialOfferArgs and go from there ... /// A vaa message is the serialised message body of a posted vaa. Only the fields that are required to create the digest are included. #[derive(AnchorSerialize, AnchorDeserialize)] pub struct VaaMessage(pub Vec); impl VaaMessage { - pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32], payload: Vec) -> Self { - Self(VaaMessageBody::new(consistency_level, vaa_time, sequence, emitter_chain, emitter_address, payload).to_vec()) + pub fn new( + consistency_level: u8, + vaa_time: u32, + sequence: u64, + emitter_chain: u16, + emitter_address: [u8; 32], + payload: Vec, + ) -> Self { + Self( + VaaMessageBody::new( + consistency_level, + vaa_time, + sequence, + emitter_chain, + emitter_address, + payload, + ) + .to_vec(), + ) } pub fn from_vec(vec: Vec) -> Self { @@ -146,12 +162,10 @@ impl VaaMessage { // emitter_address is the next 32 bytes of the message self.0[10..42].try_into().unwrap() } - } /// Just a helper struct to make the code more readable. struct VaaMessageBody { - /// Level of consistency requested by the emitter pub consistency_level: u8, @@ -175,13 +189,20 @@ struct VaaMessageBody { } impl VaaMessageBody { - pub fn new(consistency_level: u8, vaa_time: u32, sequence: u64, emitter_chain: u16, emitter_address: [u8; 32], payload: Vec) -> Self { + pub fn new( + consistency_level: u8, + vaa_time: u32, + sequence: u64, + emitter_chain: u16, + emitter_address: [u8; 32], + payload: Vec, + ) -> Self { Self { consistency_level, vaa_time, nonce: 0, // Always 0 sequence, - emitter_chain, // Can be taken from the live router path + emitter_chain, // Can be taken from the live router path emitter_address, // Can be taken from the live router path payload, } @@ -196,6 +217,7 @@ impl VaaMessageBody { &self.sequence.to_be_bytes(), &[self.consistency_level], self.payload.as_ref(), - ].concat() + ] + .concat() } -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 57fd14b37..cc694ec39 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::*; +use solana_program::keccak; #[account(zero_copy)] #[derive(Debug)] @@ -15,18 +16,58 @@ pub struct FastMarketOrder { pub max_fee: u64, pub init_auction_fee: u64, pub redeemer_message: [u8; 512], - pub digest: [u8; 32], pub refund_recipient: [u8; 32], + pub vaa_emitter_address: [u8; 32], pub vaa_sequence: u64, pub vaa_timestamp: u32, + pub vaa_nonce: u32, pub vaa_emitter_chain: u16, - pub vaa_emitter_address: [u8; 32], - _padding: [u8; 2], + pub vaa_consistency_level: u8, + _padding: [u8; 5], } impl FastMarketOrder { - pub fn new(amount_in: u64, min_amount_out: u64, deadline: u32, target_chain: u16, redeemer_message_length: u16, redeemer: [u8; 32], sender: [u8; 32], refund_address: [u8; 32], max_fee: u64, init_auction_fee: u64, redeemer_message: [u8; 512], digest: [u8; 32], refund_recipient: [u8; 32], vaa_sequence: u64, vaa_timestamp: u32, vaa_emitter_chain: u16, vaa_emitter_address: [u8; 32]) -> Self { - Self { amount_in, min_amount_out, deadline, target_chain, redeemer_message_length, redeemer, sender, refund_address, max_fee, init_auction_fee, redeemer_message, digest, refund_recipient, vaa_sequence, vaa_timestamp, vaa_emitter_chain, vaa_emitter_address, _padding: [0_u8; 2], } + pub fn new( + amount_in: u64, + min_amount_out: u64, + deadline: u32, + target_chain: u16, + redeemer_message_length: u16, + redeemer: [u8; 32], + sender: [u8; 32], + refund_address: [u8; 32], + max_fee: u64, + init_auction_fee: u64, + redeemer_message: [u8; 512], + refund_recipient: [u8; 32], + vaa_sequence: u64, + vaa_timestamp: u32, + vaa_nonce: u32, + vaa_emitter_chain: u16, + vaa_consistency_level: u8, + vaa_emitter_address: [u8; 32], + ) -> Self { + Self { + amount_in, + min_amount_out, + deadline, + target_chain, + redeemer_message_length, + redeemer, + sender, + refund_address, + max_fee, + init_auction_fee, + redeemer_message, + refund_recipient, + vaa_sequence, + vaa_timestamp, + vaa_nonce, + vaa_emitter_chain, + vaa_consistency_level, + vaa_emitter_address, + _padding: [0_u8; 5], + } } pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; @@ -37,7 +78,7 @@ impl FastMarketOrder { payload.extend_from_slice(payload_slice); payload } - + pub fn payload(&self) -> Vec { let mut payload = vec![]; payload.push(11_u8); @@ -52,9 +93,26 @@ impl FastMarketOrder { payload.extend_from_slice(&self.deadline.to_be_bytes()); payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); if self.redeemer_message_length > 0 { - payload.extend_from_slice(&self.redeemer_message[..self.redeemer_message_length as usize]); + payload + .extend_from_slice(&self.redeemer_message[..self.redeemer_message_length as usize]); } payload } -} + pub fn digest(&self) -> [u8; 32] { + let message_hash = keccak::hashv(&[ + self.vaa_timestamp.to_be_bytes().as_ref(), + self.vaa_nonce.to_be_bytes().as_ref(), + self.vaa_emitter_chain.to_be_bytes().as_ref(), + &self.vaa_emitter_address, + &self.vaa_sequence.to_be_bytes(), + &[self.vaa_consistency_level], + self.payload().as_ref(), + ]); + // Digest is the hash of the message + keccak::hashv(&[message_hash.as_ref()]) + .as_ref() + .try_into() + .unwrap() + } +} diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 7d58f6338..78d92b1ca 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,102 +1,93 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; -use common::wormhole_cctp_solana::cctp::token_messenger_minter_program::RemoteTokenMessenger; use matching_engine::state::FastMarketOrder; -use matching_engine::{ID as PROGRAM_ID, CCTP_MINT_RECIPIENT}; +use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; mod utils; use solana_sdk::signer::Signer; use solana_sdk::transaction::VersionedTransaction; -use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; -use utils::shims_prepare_order_response::{PrepareOrderResponseShimAccountsFixture, PrepareOrderResponseShimDataFixture}; -use utils::{Chain, REGISTERED_TOKEN_ROUTERS}; -use utils::router::{create_cctp_router_endpoints_test, add_local_router_endpoint_ix, create_all_router_endpoints_test}; +use utils::auction::{improve_offer, place_initial_offer, AuctionAccounts}; use utils::initialize::initialize_program; -use utils::auction::{improve_offer, place_initial_offer, AuctionAccounts, AuctionOfferFixture}; -use utils::setup::{PreTestingContext, TestingContext}; -use utils::vaa::create_vaas_test_with_chain_and_address; +use utils::router::{ + add_local_router_endpoint_ix, create_all_router_endpoints_test, + create_cctp_router_endpoints_test, +}; +use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; use utils::shims::{ - // place_initial_offer_shim, - place_initial_offer_fallback, set_up_post_message_transaction_test, initialise_fast_market_order_fallback_instruction}; + initialise_fast_market_order_fallback_instruction, place_initial_offer_fallback, + set_up_post_message_transaction_test, +}; +use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; +use utils::vaa::VaaArgs; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; -// Configures the program ID and CCTP mint recipient based on the environment -cfg_if::cfg_if! { - if #[cfg(feature = "mainnet")] { - //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"); - //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); - const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); - const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint.json"; - } else if #[cfg(feature = "testnet")] { - //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); - //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); - const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; - } else if #[cfg(feature = "localnet")] { - //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); - // const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); - } -} -const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; - /// Test that the program is initialised correctly #[tokio::test] pub async fn test_initialize_program() { - let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let testing_context = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let initialize_fixture = initialize_program(&testing_context).await; // Check that custodian data corresponds to the expected values - initialize_fixture.verify_custodian(testing_context.testing_actors.owner.pubkey(), testing_context.testing_actors.owner_assistant.pubkey(), testing_context.testing_actors.fee_recipient.token_account.unwrap().address); + initialize_fixture.verify_custodian( + testing_context.testing_actors.owner.pubkey(), + testing_context.testing_actors.owner_assistant.pubkey(), + testing_context + .testing_actors + .fee_recipient + .token_account + .unwrap() + .address, + ); } /// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains #[tokio::test] pub async fn test_cctp_token_router_endpoint_creation() { - let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let testing_context = setup_environment( + ShimMode::None, // Shim mode + TransferDirection::FromArbitrumToEthereum, // Transfer direction + None, // Vaa args + ) + .await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - - let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); - let arb_remote_token_messenger = fixture_accounts.arbitrum_remote_token_messenger; - let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; + let initialize_fixture = initialize_program(&testing_context).await; - let usdc_mint_address = USDC_MINT_ADDRESS; - let token_router_endpoints = create_cctp_router_endpoints_test( - &testing_context.test_context, + &testing_context, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), - arb_remote_token_messenger, - eth_remote_token_messenger, - usdc_mint_address, testing_context.testing_actors.owner.keypair(), - PROGRAM_ID, - ).await; + ) + .await; assert_eq!(token_router_endpoints.len(), 2); } #[tokio::test] pub async fn test_local_token_router_endpoint_creation() { - let pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - - let initialize_fixture: utils::initialize::InitializeFixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; + let testing_context = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; - let usdc_mint_address = USDC_MINT_ADDRESS; + let initialize_fixture = initialize_program(&testing_context).await; let _local_token_router_endpoint = add_local_router_endpoint_ix( - &testing_context.test_context, + &testing_context, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), testing_context.testing_actors.owner.keypair().as_ref(), - PROGRAM_ID, - &usdc_mint_address, - ).await; + ) + .await; } // Test setting up vaas @@ -104,84 +95,114 @@ pub async fn test_local_token_router_endpoint_creation() { // - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs #[tokio::test] pub async fn test_setup_vaas() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, None, true); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - let first_test_ft = vaas_test.0.first().unwrap(); - first_test_ft.verify_vaas(&testing_context.test_context).await; - - // Get the fixture accounts - let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let mut testing_context = + setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; + + testing_context.verify_vaas().await; + let initialize_fixture = initialize_program(&testing_context).await; + // Try making initial offer - let fast_vaa = first_test_ft.fast_transfer_vaa.get_vaa_pubkey(); - let usdc_mint_address = USDC_MINT_ADDRESS; + let fast_vaa = testing_context + .get_vaa_pair(0) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); let auction_config_address = initialize_fixture.get_auction_config_address(); let router_endpoints = create_all_router_endpoints_test( - &testing_context.test_context, + &testing_context, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), - fixture_accounts.arbitrum_remote_token_messenger, - fixture_accounts.ethereum_remote_token_messenger, - usdc_mint_address, testing_context.testing_actors.owner.keypair(), - PROGRAM_ID, - ).await; - let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; - let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + ) + .await; let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( - Some(fast_vaa), // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - arb_endpoint_address, // From router endpoint pubkey - eth_endpoint_address, // To router endpoint pubkey + Some(fast_vaa_pubkey), // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey + testing_context.get_usdc_mint_address(), // USDC mint pubkey + transfer_direction, ); - let fast_market_order = first_test_ft.fast_transfer_vaa.clone(); - - let initial_offer_fixture = place_initial_offer(&testing_context.test_context, &auction_accounts, fast_market_order, testing_context.testing_actors.owner.keypair(), PROGRAM_ID).await; - initial_offer_fixture.verify_initial_offer(&testing_context.test_context).await; - - let _improved_offer_fixture = improve_offer(&testing_context.test_context, initial_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, solver, auction_config_address).await; - // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; + place_initial_offer(&mut testing_context, auction_accounts, fast_vaa, PROGRAM_ID).await; + let auction_state = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap(); + auction_state + .verify_initial_offer(&testing_context.test_context) + .await; + + improve_offer( + &mut testing_context, + PROGRAM_ID, + solver, + auction_config_address, + ) + .await; + // TODO: Implement check on improved offer auction state + // auction_state + // .borrow() + // .verify_improved_offer(&testing_context.test_context) + // .await; } - #[tokio::test] pub async fn test_post_message_shims() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - // Add shim programs - pre_testing_context.add_post_message_shims(); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; let actors = testing_context.testing_actors; let emitter_signer = actors.owner.keypair(); let payer_signer = actors.solvers[0].keypair(); - let recent_blockhash = testing_context.test_context.borrow().last_blockhash; - set_up_post_message_transaction_test(&testing_context.test_context, &payer_signer, &emitter_signer, recent_blockhash).await; + set_up_post_message_transaction_test( + &testing_context.test_context, + &payer_signer, + &emitter_signer, + ) + .await; } #[tokio::test] pub async fn test_initialise_fast_market_order_fallback() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - - // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let first_test_ft = vaas_test.0.first().unwrap(); + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; let solver = testing_context.testing_actors.solvers[0].clone(); - - let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; + + let vaa_data = first_test_ft.vaa_data; + let (fast_market_order, vaa_data) = + utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = + utils::shims::create_guardian_signatures( + &testing_context.test_context, + &testing_context.testing_actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await; let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( &testing_context.testing_actors.owner.keypair(), @@ -192,27 +213,50 @@ pub async fn test_initialise_fast_market_order_fallback() { guardian_set_bump, ); let recent_blockhash = testing_context.test_context.borrow().last_blockhash; - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer(&[initialise_fast_market_order_ix], Some(&testing_context.testing_actors.owner.pubkey()), &[&testing_context.testing_actors.owner.keypair()], recent_blockhash); - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - testing_context.test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to initialise fast market order"); + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( + &[initialise_fast_market_order_ix], + Some(&testing_context.testing_actors.owner.pubkey()), + &[&testing_context.testing_actors.owner.keypair()], + recent_blockhash, + ); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + testing_context + .test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .expect("Failed to initialise fast market order"); } #[tokio::test] pub async fn test_close_fast_market_order_fallback() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - - // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let first_test_ft = vaas_test.0.first().unwrap(); + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; let solver = testing_context.testing_actors.solvers[0].clone(); - - let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - let (fast_market_order, vaa_data) = utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; + + let vaa_data = first_test_ft.vaa_data; + let (fast_market_order, vaa_data) = + utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = + utils::shims::create_guardian_signatures( + &testing_context.test_context, + &testing_context.testing_actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await; let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( &testing_context.testing_actors.owner.keypair(), @@ -224,49 +268,117 @@ pub async fn test_close_fast_market_order_fallback() { ); let recent_blockhash = testing_context.test_context.borrow().last_blockhash; // Get balance of solver before initialising fast market order - let solver_balance_before = testing_context.test_context.borrow_mut().banks_client.get_balance(solver.pubkey()).await.expect("Failed to get balance of solver"); - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer(&[initialise_fast_market_order_ix], Some(&testing_context.testing_actors.owner.pubkey()), &[&testing_context.testing_actors.owner.keypair()], recent_blockhash); - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - testing_context.test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.expect("Failed to initialise fast market order"); - let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrder::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], &PROGRAM_ID).0; - utils::shims::close_fast_market_order_fallback(&testing_context.test_context, &solver.keypair(), &PROGRAM_ID, &fast_market_order_account).await; - let solver_balance_after = testing_context.test_context.borrow_mut().banks_client.get_balance(solver.pubkey()).await.expect("Failed to get balance of solver"); + let solver_balance_before = testing_context + .test_context + .borrow_mut() + .banks_client + .get_balance(solver.pubkey()) + .await + .expect("Failed to get balance of solver"); + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( + &[initialise_fast_market_order_ix], + Some(&testing_context.testing_actors.owner.pubkey()), + &[&testing_context.testing_actors.owner.keypair()], + recent_blockhash, + ); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + testing_context + .test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .expect("Failed to initialise fast market order"); + let fast_market_order_account = Pubkey::find_program_address( + &[ + FastMarketOrder::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.refund_recipient, + ], + &PROGRAM_ID, + ) + .0; + utils::shims::close_fast_market_order_fallback( + &testing_context.test_context, + &solver.keypair(), + &PROGRAM_ID, + &fast_market_order_account, + ) + .await; + let solver_balance_after = testing_context + .test_context + .borrow_mut() + .banks_client + .get_balance(solver.pubkey()) + .await + .expect("Failed to get balance of solver"); assert!(solver_balance_after > solver_balance_before, "Solver balance before initialising fast market order was {:?}, but after closing it was {:?}, though it should have been greater", solver_balance_before, solver_balance_after); } #[tokio::test] pub async fn test_approve_usdc() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - pre_testing_context.add_post_message_shims(); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. - let vaa_data = { - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); - let first_test_ft = vaas_test.0.first().unwrap(); - first_test_ft.fast_transfer_vaa.clone().vaa_data + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() }; - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let vaa_data = first_test_ft.vaa_data; let actors = testing_context.testing_actors; let solver = actors.solvers[0].clone(); let offer_price: u64 = 1__000_000; let program_id = PROGRAM_ID; let new_pubkey = Pubkey::new_unique(); - - let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &new_pubkey.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; - solver.approve_usdc(&testing_context.test_context, &transfer_authority, offer_price).await; - + + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &new_pubkey.to_bytes(), + &offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + solver + .approve_usdc( + &testing_context.test_context, + &transfer_authority, + offer_price, + ) + .await; + let usdc_balance = solver.get_balance(&testing_context.test_context).await; // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; - + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = + utils::shims::create_guardian_signatures( + &testing_context.test_context, + &actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await; + println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); - let solver_token_account_info = testing_context.test_context.borrow_mut().banks_client.get_account(solver_token_account_address).await.expect("Failed to query banks client for solver token account info").expect("Failed to get solver token account info"); - let solver_token_account = TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); + let solver_token_account_info = testing_context + .test_context + .borrow_mut() + .banks_client + .get_account(solver_token_account_address) + .await + .expect("Failed to query banks client for solver token account info") + .expect("Failed to get solver token account info"); + let solver_token_account = + TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); assert!(solver_token_account.delegate.is_some()); } @@ -274,71 +386,70 @@ pub async fn test_approve_usdc() { // Testing a initial offer from arbitrum to ethereum // TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction pub async fn test_place_initial_offer_fallback() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - pre_testing_context.add_post_message_shims(); - // This will create vaas for the arbitrum and ethereum chains and post them to the test context accounts. These vaas will not be needed for the shim test, and shouldn't interact with the program during the test. - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - - // This will create the fast transfer and deposit vaas but will not post them. Both will have nonce == 0. Deposit vaa will have sequence == 0, fast transfer vaa will have sequence == 1. - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0),false); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - - let first_test_ft = vaas_test.0.first().unwrap(); - - let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let initialize_fixture = initialize_program(&testing_context).await; + + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + // Try making initial offer using the shim instruction - let usdc_mint_address = USDC_MINT_ADDRESS; + let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_config_address = initialize_fixture.get_auction_config_address(); let router_endpoints = create_all_router_endpoints_test( - &testing_context.test_context, + &testing_context, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), - fixture_accounts.arbitrum_remote_token_messenger, - fixture_accounts.ethereum_remote_token_messenger, - usdc_mint_address, testing_context.testing_actors.owner.keypair(), - PROGRAM_ID, - ).await; - let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; - let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; + ) + .await; let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - arb_endpoint_address, // From router endpoint pubkey - eth_endpoint_address, // To router endpoint pubkey + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey + usdc_mint_address, // USDC mint pubkey + transfer_direction, ); - - let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; + + let vaa_data = first_test_ft.vaa_data; // Place initial offer using the fallback program - let initial_offer_fixture = place_initial_offer_fallback( - &testing_context.test_context, - &testing_context.testing_actors.owner.keypair(), + let payer_signer = testing_context.testing_actors.owner.keypair(); + let _initial_offer_fixture = place_initial_offer_fallback( + &mut testing_context, + &payer_signer, &PROGRAM_ID, &CORE_BRIDGE_PROGRAM_ID, &vaa_data, solver.clone(), &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) - ).await.expect("Failed to place initial offer"); - let auction_offer_fixture = AuctionOfferFixture { - auction_address: initial_offer_fixture.auction_address, - auction_custody_token_address: initial_offer_fixture.auction_custody_token_address, - offer_price: 1__000_000, - offer_token: auction_accounts.offer_token, - }; + ) + .await + .expect("Failed to place initial offer"); // Attempt to improve the offer using the non-fallback method with another solver making the improved offer println!("Improving offer"); let second_solver = testing_context.testing_actors.solvers[1].clone(); - let _improved_offer_fixture = improve_offer(&testing_context.test_context, auction_offer_fixture, testing_context.testing_actors.owner.keypair(), PROGRAM_ID, second_solver, auction_config_address).await; + improve_offer( + &mut testing_context, + PROGRAM_ID, + second_solver, + auction_config_address, + ) + .await; println!("Offer improved"); // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; } @@ -347,51 +458,54 @@ pub async fn test_place_initial_offer_fallback() { // Testing an execute order from arbitrum to ethereum // TODO: Flesh out this test to see if the message was posted correctly pub async fn test_execute_order_fallback() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - pre_testing_context.add_post_message_shims(); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0), false); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - let actors = testing_context.testing_actors; - let payer_signer = actors.owner.keypair(); - let first_test_ft = vaas_test.0.first().unwrap(); - let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); - + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let initialize_fixture = initialize_program(&testing_context).await; + + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction - let usdc_mint_address = USDC_MINT_ADDRESS; + let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_config_address = initialize_fixture.get_auction_config_address(); let router_endpoints = create_all_router_endpoints_test( - &testing_context.test_context, - actors.owner.pubkey(), + &testing_context, + testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), - fixture_accounts.arbitrum_remote_token_messenger, - fixture_accounts.ethereum_remote_token_messenger, - usdc_mint_address, - actors.owner.keypair(), - PROGRAM_ID, - ).await; - let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; - let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; - let solver: utils::setup::Solver = actors.solvers[0].clone(); + testing_context.testing_actors.owner.keypair(), + ) + .await; + let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - arb_endpoint_address, // From router endpoint pubkey - eth_endpoint_address, // To router endpoint pubkey + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey + usdc_mint_address, // USDC mint pubkey + transfer_direction, ); - let vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - + let vaa_data = first_test_ft.vaa_data; + + let payer_signer = testing_context.testing_actors.owner.keypair(); + // Place initial offer using the fallback program let initial_offer_fixture = place_initial_offer_fallback( - &testing_context.test_context, + &mut testing_context, &payer_signer, &PROGRAM_ID, &CORE_BRIDGE_PROGRAM_ID, @@ -399,11 +513,22 @@ pub async fn test_execute_order_fallback() { solver.clone(), &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) - ).await.expect("Failed to place initial offer"); + ) + .await + .expect("Failed to place initial offer"); + + println!( + "Solver balance after placing initial offer: {:?}", + solver.get_balance(&testing_context.test_context).await + ); - println!("Solver balance after placing initial offer: {:?}", solver.get_balance(&testing_context.test_context).await); - - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture, &payer_signer.pubkey(), &fixture_accounts); + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + &auction_accounts, + &initial_offer_fixture, + &payer_signer.pubkey(), + &fixture_accounts, + transfer_direction, + ); // Try executing the order using the fallback program let _execute_order_fixture = execute_order_fallback( &testing_context.test_context, @@ -411,71 +536,211 @@ pub async fn test_execute_order_fallback() { &PROGRAM_ID, solver.clone(), &execute_order_fallback_accounts, - ).await.expect("Failed to execute order"); + ) + .await + .expect("Failed to execute order"); // Figure out why the solver balance is not increased here - println!("Solver balance after executing order: {:?}", solver.get_balance(&testing_context.test_context).await); + println!( + "Solver balance after executing order: {:?}", + solver.get_balance(&testing_context.test_context).await + ); } +// From ethereum to arbitrum #[tokio::test] pub async fn test_prepare_order_shim_fallback() { - let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); - pre_testing_context.add_verify_shims(); - pre_testing_context.add_post_message_shims(); - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); - let vaas_test = create_vaas_test_with_chain_and_address(&mut pre_testing_context.program_test, USDC_MINT_ADDRESS, None, CCTP_MINT_RECIPIENT, Chain::Arbitrum, Chain::Ethereum, arbitrum_emitter_address, ethereum_emitter_address, None, Some(0), false); - let testing_context = TestingContext::new(pre_testing_context, USDC_MINT_FIXTURE_PATH, USDC_MINT_ADDRESS).await; - let initialize_fixture = initialize_program(&testing_context, PROGRAM_ID, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT).await; - let actors = testing_context.testing_actors; - let payer_signer = actors.owner.keypair(); - let first_test_ft = vaas_test.0.first().unwrap(); - let fixture_accounts = testing_context.fixture_accounts.expect("Pre-made fixture accounts not found"); - + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let initialize_fixture = initialize_program(&testing_context).await; + + let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); + let payload_deserialized: utils::vaa::PayloadDeserialized = first_vaa_pair + .deposit_vaa + .clone() + .payload_deserialized + .unwrap(); + let deposit = payload_deserialized.get_deposit().unwrap(); + + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Pre-made fixture accounts not found"); // Try making initial offer using the shim instruction - let usdc_mint_address = USDC_MINT_ADDRESS; + let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_config_address = initialize_fixture.get_auction_config_address(); let router_endpoints = create_all_router_endpoints_test( - &testing_context.test_context, - actors.owner.pubkey(), + &testing_context, + testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), - fixture_accounts.arbitrum_remote_token_messenger, - fixture_accounts.ethereum_remote_token_messenger, - usdc_mint_address, - actors.owner.keypair(), - PROGRAM_ID, - ).await; - let arb_endpoint_address = router_endpoints.arbitrum.endpoint_address; - let eth_endpoint_address = router_endpoints.ethereum.endpoint_address; - let solver: utils::setup::Solver = actors.solvers[0].clone(); + testing_context.testing_actors.owner.keypair(), + ) + .await; + let solver = testing_context.testing_actors.solvers[0].clone(); let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - arb_endpoint_address, // From router endpoint pubkey - eth_endpoint_address, // To router endpoint pubkey + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey + usdc_mint_address, // USDC mint pubkey + transfer_direction, ); - let ft_vaa_data = first_test_ft.fast_transfer_vaa.clone().vaa_data; - + let fast_transfer_vaa_data = first_vaa_pair.fast_transfer_vaa.vaa_data; + let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; + + let payer_signer = testing_context.testing_actors.owner.keypair(); + // Place initial offer using the fallback program let initial_offer_fixture = place_initial_offer_fallback( + &mut testing_context, + &payer_signer, + &PROGRAM_ID, + &CORE_BRIDGE_PROGRAM_ID, + &fast_transfer_vaa_data, + solver.clone(), + &auction_accounts, + 1__000_000, // 1 USDC (double underscore for decimal separator) + ) + .await + .expect("Failed to place initial offer"); + + println!( + "Solver balance after placing initial offer: {:?}", + solver.get_balance(&testing_context.test_context).await + ); + + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + &auction_accounts, + &initial_offer_fixture, + &payer_signer.pubkey(), + &fixture_accounts, + transfer_direction, + ); + // Try executing the order using the fallback program + let execute_order_fixture = execute_order_fallback( + &testing_context.test_context, + &payer_signer, + &PROGRAM_ID, + solver.clone(), + &execute_order_fallback_accounts, + ) + .await + .expect("Failed to execute order"); + + utils::shims_prepare_order_response::prepare_order_response_test( &testing_context.test_context, &payer_signer, + &deposit_vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + &PROGRAM_ID, + &fixture_accounts, + &execute_order_fixture, + &initial_offer_fixture, + &initialize_fixture, + &router_endpoints.ethereum.endpoint_address, + &router_endpoints.arbitrum.endpoint_address, + &usdc_mint_address, + &CCTP_MINT_RECIPIENT, + &initialize_fixture.get_custodian_address(), + &deposit, + ) + .await + .expect("Failed to prepare order response"); +} + +#[tokio::test] +pub async fn test_settle_auction_complete() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let initialize_fixture = initialize_program(&testing_context).await; + + let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); + + let payload_deserialized: utils::vaa::PayloadDeserialized = first_vaa_pair + .deposit_vaa + .clone() + .payload_deserialized + .unwrap(); + let deposit = payload_deserialized.get_deposit().unwrap(); + + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Pre-made fixture accounts not found"); + // Try making initial offer using the shim instruction + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = create_all_router_endpoints_test( + &testing_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + testing_context.testing_actors.owner.keypair(), + ) + .await; + + let solver = testing_context.testing_actors.solvers[0].clone(); + let auction_accounts = AuctionAccounts::new( + None, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + transfer_direction, + ); + + let fast_transfer_vaa_data = first_vaa_pair.fast_transfer_vaa.vaa_data; + let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; + + let payer_signer = testing_context.testing_actors.owner.keypair(); + + // Place initial offer using the fallback program + let initial_offer_fixture = place_initial_offer_fallback( + &mut testing_context, + &payer_signer, &PROGRAM_ID, &CORE_BRIDGE_PROGRAM_ID, - &ft_vaa_data, + &fast_transfer_vaa_data, solver.clone(), &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) - ).await.expect("Failed to place initial offer"); + ) + .await + .expect("Failed to place initial offer"); + + println!( + "Solver balance after placing initial offer: {:?}", + solver.get_balance(&testing_context.test_context).await + ); - let deposit_vaa_data = first_test_ft.deposit_vaa.clone().vaa_data; - - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(&auction_accounts, &initial_offer_fixture, &payer_signer.pubkey(), &fixture_accounts); + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + &auction_accounts, + &initial_offer_fixture, + &payer_signer.pubkey(), + &fixture_accounts, + transfer_direction, + ); // Try executing the order using the fallback program let execute_order_fixture = execute_order_fallback( &testing_context.test_context, @@ -483,15 +748,39 @@ pub async fn test_prepare_order_shim_fallback() { &PROGRAM_ID, solver.clone(), &execute_order_fallback_accounts, - ).await.expect("Failed to execute order"); - - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = utils::shims::create_guardian_signatures(&testing_context.test_context, &payer_signer, &deposit_vaa_data, &CORE_BRIDGE_PROGRAM_ID, None).await; - let arbitrum_token_messenger_data = testing_context.test_context.borrow_mut().banks_client.get_account(fixture_accounts.arbitrum_remote_token_messenger).await.unwrap().unwrap().data; - let ethereum_token_messenger_data = testing_context.test_context.borrow_mut().banks_client.get_account(fixture_accounts.ethereum_remote_token_messenger).await.unwrap().unwrap().data; - let source_remote_token_messenger = RemoteTokenMessenger::try_deserialize(&mut arbitrum_token_messenger_data.as_ref()).unwrap(); - let destination_remote_token_messenger = RemoteTokenMessenger::try_deserialize(&mut ethereum_token_messenger_data.as_ref()).unwrap(); - let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new(encoded_cctp_message, cctp_attestation) - let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new(&payer_signer, &fixture_accounts, &execute_order_fallback_accounts, &initial_offer_fixture, &initialize_fixture, &arb_endpoint_address, ð_endpoint_address, &usdc_mint_address, &cctp_message_decoded, &guardian_set_pubkey, &guardian_signatures_pubkey); - prepare_order_response_cctp_shim(&testing_context.test_context, &payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, &PROGRAM_ID).await; - -} \ No newline at end of file + ) + .await + .expect("Failed to execute order"); + + let prepare_order_response_shim_fixture = + utils::shims_prepare_order_response::prepare_order_response_test( + &testing_context.test_context, + &payer_signer, + &deposit_vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + &PROGRAM_ID, + &fixture_accounts, + &execute_order_fixture, + &initial_offer_fixture, + &initialize_fixture, + &router_endpoints.ethereum.endpoint_address, + &router_endpoints.arbitrum.endpoint_address, + &usdc_mint_address, + &CCTP_MINT_RECIPIENT, + &initialize_fixture.get_custodian_address(), + &deposit, + ) + .await + .expect("Failed to prepare order response"); + let auction_state = initial_offer_fixture.auction_state; + utils::settle_auction::settle_auction_complete( + &testing_context.test_context, + &payer_signer, + &usdc_mint_address, + &prepare_order_response_shim_fixture, + &auction_state, + &PROGRAM_ID, + ) + .await + .expect("Failed to settle auction"); +} diff --git a/solana/programs/matching-engine/tests/utils/account_fixtures.rs b/solana/programs/matching-engine/tests/utils/account_fixtures.rs index 591a5ed7a..9f7aa6d2d 100644 --- a/solana/programs/matching-engine/tests/utils/account_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/account_fixtures.rs @@ -1,9 +1,9 @@ use anchor_lang::prelude::{pubkey, Pubkey}; -use solana_program_test::ProgramTest; use serde_json::Value; +use solana_program_test::ProgramTest; use std::{fs, str::FromStr}; -#[allow(dead_code)] +#[derive(Clone)] pub struct FixtureAccounts { // Accounts/Core pub core_bridge_config: Pubkey, @@ -65,7 +65,12 @@ impl FixtureAccounts { pub fn add_lookup_table_hack(program_test: &mut ProgramTest) { let filename = "tests/fixtures/lup.json"; let account_fixture = read_account_from_file(filename); - program_test.add_account_with_file_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, filename); + program_test.add_account_with_file_data( + account_fixture.address, + account_fixture.lamports, + account_fixture.owner, + filename, + ); } } @@ -77,14 +82,16 @@ impl FixtureAccounts { /// /// * `program_test` - The program test instance /// * `filename` - The path to the JSON fixture file -fn add_account_from_file( - program_test: &mut ProgramTest, - filename: &str, -) -> AccountFixture { +fn add_account_from_file(program_test: &mut ProgramTest, filename: &str) -> AccountFixture { // Parse the JSON file to an AccountFixture struct let account_fixture = read_account_from_file(filename); // Add the account to the program test - program_test.add_account_with_base64_data(account_fixture.address, account_fixture.lamports, account_fixture.owner, &account_fixture.base_64_data); + program_test.add_account_with_base64_data( + account_fixture.address, + account_fixture.lamports, + account_fixture.owner, + &account_fixture.base_64_data, + ); account_fixture } @@ -108,32 +115,41 @@ struct AccountFixture { /// # Returns /// /// An AccountFixture struct containing the address, owner, lamports, and filename. -fn read_account_from_file( - filename: &str, -) -> AccountFixture { +fn read_account_from_file(filename: &str) -> AccountFixture { // Read the JSON file - let data = fs::read_to_string(filename) - .expect(&format!("Unable to read file {}", filename)); + let data = fs::read_to_string(filename).expect(&format!("Unable to read file {}", filename)); // Parse the JSON - let json: Value = serde_json::from_str(&data) - .expect(&format!("Unable to parse JSON {}", filename)); + let json: Value = + serde_json::from_str(&data).expect(&format!("Unable to parse JSON {}", filename)); // Extract the lamports value let lamports = json["account"]["lamports"] - .as_u64() - .expect(&format!("lamports field not found or invalid {}", filename)); + .as_u64() + .expect(&format!("lamports field not found or invalid {}", filename)); // Extract the address value - let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["pubkey"].as_str().expect("pubkey field not found or invalid")).expect("Pubkey field in file is not a valid pubkey"); + let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str( + json["pubkey"] + .as_str() + .expect("pubkey field not found or invalid"), + ) + .expect("Pubkey field in file is not a valid pubkey"); // Extract the owner address value - let owner: Pubkey = solana_sdk::pubkey::Pubkey::from_str(json["account"]["owner"].as_str().expect("owner field not found or invalid")).expect("Owner field in file is not a valid pubkey"); + let owner: Pubkey = solana_sdk::pubkey::Pubkey::from_str( + json["account"]["owner"] + .as_str() + .expect("owner field not found or invalid"), + ) + .expect("Owner field in file is not a valid pubkey"); - let base_64_data = json["account"]["data"][0].as_str().expect("data field not found or invalid"); + let base_64_data = json["account"]["data"][0] + .as_str() + .expect("data field not found or invalid"); AccountFixture { address, owner, lamports, base_64_data: base_64_data.to_string(), } -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index d4aef5a12..79d42516f 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -1,13 +1,9 @@ use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; -use std::rc::Rc; -use std::cell::RefCell; -use solana_sdk::{ - pubkey::Pubkey, - system_instruction, - signature::Signer, -}; use solana_sdk::transaction::{Transaction, VersionedTransaction}; +use solana_sdk::{pubkey::Pubkey, signature::Signer, system_instruction}; +use std::cell::RefCell; +use std::rc::Rc; use super::constants; @@ -25,13 +21,9 @@ pub async fn airdrop( amount: u64, ) { let mut ctx = test_context.borrow_mut(); - + // Create the transfer instruction with values from the context - let transfer_ix = system_instruction::transfer( - &ctx.payer.pubkey(), - recipient, - amount, - ); + let transfer_ix = system_instruction::transfer(&ctx.payer.pubkey(), recipient, amount); // Create and send transaction let tx = Transaction::new_signed_with_payer( @@ -49,7 +41,11 @@ pub async fn airdrop_usdc( recipient_ata: &Pubkey, amount: u64, ) { - let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); + let new_blockhash = test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); let usdc_mint_address = constants::USDC_MINT; let mint_to_ix = spl_token::instruction::mint_to( &spl_token::ID, @@ -57,8 +53,9 @@ pub async fn airdrop_usdc( recipient_ata, &test_context.borrow().payer.pubkey(), &[], - amount - ).expect("Failed to create mint to instruction"); + amount, + ) + .expect("Failed to create mint to instruction"); let tx = Transaction::new_signed_with_payer( &[mint_to_ix.clone()], Some(&test_context.borrow().payer.pubkey()), @@ -66,7 +63,13 @@ pub async fn airdrop_usdc( new_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(tx).expect("Failed to convert transaction to versioned transaction"); + let versioned_transaction = VersionedTransaction::try_from(tx) + .expect("Failed to convert transaction to versioned transaction"); - test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); + test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .unwrap(); } diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index dc71e80f4..745fef1fe 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,19 +1,25 @@ use anchor_lang::prelude::*; -use matching_engine::state::{Auction, AuctionInfo}; -use matching_engine::instruction::{PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, ImproveOffer as ImproveOfferIx}; -use matching_engine::accounts::{ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc}; +use super::router::TestRouterEndpoints; +use super::setup::{Solver, TestingContext, TransferDirection}; +use super::vaa::TestVaa; +use anchor_lang::InstructionData; +use common::TRANSFER_AUTHORITY_SEED_PREFIX; use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; +use matching_engine::accounts::{ + ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, + LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc, +}; +use matching_engine::instruction::{ + ImproveOffer as ImproveOfferIx, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, +}; +use matching_engine::state::{Auction, AuctionInfo}; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; +use solana_sdk::signature::Signer; +use solana_sdk::transaction::Transaction; use std::cell::RefCell; use std::rc::Rc; -use solana_program_test::ProgramTestContext; -use solana_sdk::transaction::Transaction; -use solana_sdk::signature::{Keypair, Signer}; -use common::TRANSFER_AUTHORITY_SEED_PREFIX; -use anchor_lang::InstructionData; -use super::setup::Solver; -use super::vaa::TestVaa; pub struct AuctionAccounts { pub fast_vaa: Option, @@ -26,8 +32,57 @@ pub struct AuctionAccounts { pub usdc_mint: Pubkey, } +pub enum AuctionState { + Active(ActiveAuctionState), + Inactive, +} + +impl AuctionState { + pub fn get_active_auction(&self) -> Option<&ActiveAuctionState> { + match self { + AuctionState::Active(auction) => Some(auction), + AuctionState::Inactive => None, + } + } + + pub fn get_active_auction_mut(&mut self) -> Option<&mut ActiveAuctionState> { + match self { + AuctionState::Active(auction) => Some(auction), + AuctionState::Inactive => None, + } + } +} +pub struct ActiveAuctionState { + pub auction_address: Pubkey, + pub auction_custody_token_address: Pubkey, + pub best_offer: AuctionOffer, +} + +pub struct AuctionOffer { + pub best_offer_token: Pubkey, + pub best_offer_price: u64, +} + impl AuctionAccounts { - pub fn new(fast_vaa: Option, solver: Solver, auction_config: Pubkey, from_router_endpoint: Pubkey, to_router_endpoint: Pubkey, custodian: Pubkey, usdc_mint_address: Pubkey) -> Self { + pub fn new( + fast_vaa: Option, + solver: Solver, + auction_config: Pubkey, + router_endpoints: &TestRouterEndpoints, + custodian: Pubkey, + usdc_mint: Pubkey, + direction: TransferDirection, + ) -> Self { + let (from_router_endpoint, to_router_endpoint) = match direction { + TransferDirection::FromEthereumToArbitrum => ( + router_endpoints.ethereum.endpoint_address, + router_endpoints.arbitrum.endpoint_address, + ), + TransferDirection::FromArbitrumToEthereum => ( + router_endpoints.arbitrum.endpoint_address, + router_endpoints.ethereum.endpoint_address, + ), + }; Self { fast_vaa, offer_token: solver.token_account_address().unwrap(), @@ -36,24 +91,24 @@ impl AuctionAccounts { from_router_endpoint, to_router_endpoint, custodian, - usdc_mint: usdc_mint_address, + usdc_mint, } } } -pub struct AuctionOfferFixture { - pub auction_address: Pubkey, - pub auction_custody_token_address: Pubkey, - pub offer_price: u64, - pub offer_token: Pubkey, -} - -impl AuctionOfferFixture { +impl ActiveAuctionState { // TODO: Figure this out pub async fn verify_initial_offer(&self, testing_context: &Rc>) { - let auction_account = testing_context.borrow_mut().banks_client.get_account(self.auction_address).await.unwrap().expect("Failed to get auction account"); + let auction_account = testing_context + .borrow_mut() + .banks_client + .get_account(self.auction_address) + .await + .unwrap() + .expect("Failed to get auction account"); let mut data_ref = auction_account.data.as_ref(); - let auction_account_data : Auction = AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + let auction_account_data: Auction = + AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); let auction_info = auction_account_data.info.unwrap(); let expected_auction_info = AuctionInfo { config_id: 0, @@ -70,26 +125,49 @@ impl AuctionOfferFixture { destination_asset_info: None, }; assert_eq!(auction_info.config_id, expected_auction_info.config_id); - assert_eq!(auction_info.vaa_sequence, expected_auction_info.vaa_sequence); - assert_eq!(auction_info.source_chain, expected_auction_info.source_chain); + assert_eq!( + auction_info.vaa_sequence, + expected_auction_info.vaa_sequence + ); + assert_eq!( + auction_info.source_chain, + expected_auction_info.source_chain + ); assert_eq!(auction_info.start_slot, expected_auction_info.start_slot); assert_eq!(auction_info.amount_in, expected_auction_info.amount_in); - assert_eq!(auction_info.security_deposit, expected_auction_info.security_deposit); + assert_eq!( + auction_info.security_deposit, + expected_auction_info.security_deposit + ); assert_eq!(auction_info.offer_price, expected_auction_info.offer_price); - assert_eq!(auction_info.redeemer_message_len, expected_auction_info.redeemer_message_len); + assert_eq!( + auction_info.redeemer_message_len, + expected_auction_info.redeemer_message_len + ); } } pub async fn place_initial_offer( - testing_context: &Rc>, - accounts: &AuctionAccounts, + testing_context: &mut TestingContext, + accounts: AuctionAccounts, fast_market_order: TestVaa, - owner_keypair: Rc, program_id: Pubkey, -) -> AuctionOfferFixture { - - let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id).0; - let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; +) { + let test_ctx = &testing_context.test_context; + let owner_keypair = testing_context.testing_actors.owner.keypair(); + let auction_address = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], + &program_id, + ) + .0; + let auction_custody_token_address = Pubkey::find_program_address( + &[ + matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_address.as_ref(), + ], + &program_id, + ) + .0; let initial_offer_ix = PlaceInitialOfferCctpIx { offer_price: 1__000_000, }; @@ -109,8 +187,19 @@ pub async fn place_initial_offer( }; let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; - let transfer_authority = Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &initial_offer_ix.offer_price.to_be_bytes()], &program_id).0; - accounts.solver.approve_usdc(testing_context, &transfer_authority, 420_000__000_000).await; + let transfer_authority = Pubkey::find_program_address( + &[ + TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &initial_offer_ix.offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + accounts + .solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; let custodian = CheckedCustodian { custodian: accounts.custodian, }; @@ -123,21 +212,23 @@ pub async fn place_initial_offer( auction: auction_address, offer_token: accounts.offer_token, auction_custody_token: auction_custody_token_address, - usdc: Usdc { mint: accounts.usdc_mint }, + usdc: Usdc { + mint: accounts.usdc_mint, + }, system_program: anchor_lang::system_program::ID, token_program: anchor_spl::token::ID, program: program_id, event_authority, }; - + let mut account_metas = initial_offer_accounts.to_account_metas(None); for meta in account_metas.iter_mut() { if meta.pubkey == accounts.offer_token { meta.is_writable = true; } } - - let initial_offer_ix_anchor = Instruction{ + + let initial_offer_ix_anchor = Instruction { program_id: program_id, accounts: account_metas, data: initial_offer_ix.data(), @@ -147,48 +238,67 @@ pub async fn place_initial_offer( &[initial_offer_ix_anchor], Some(&owner_keypair.pubkey()), &[&owner_keypair], - testing_context.borrow().last_blockhash, + test_ctx.borrow().last_blockhash, ); - - testing_context.borrow_mut().banks_client.process_transaction(tx).await.expect("Failed to place initial offer"); - AuctionOfferFixture { + test_ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await + .expect("Failed to place initial offer"); + + testing_context.testing_state.auction_state = AuctionState::Active(ActiveAuctionState { auction_address, auction_custody_token_address, - offer_price: initial_offer_ix.offer_price, - offer_token: accounts.offer_token, - } + best_offer: AuctionOffer { + best_offer_token: accounts.offer_token, + best_offer_price: initial_offer_ix.offer_price, + }, + }); } - - pub async fn improve_offer( - testing_context: &Rc>, - initial_offer_fixture: AuctionOfferFixture, - owner_keypair: Rc, + testing_context: &mut TestingContext, program_id: Pubkey, solver: Solver, auction_config: Pubkey, -) -> AuctionOfferFixture { - - let auction_address = initial_offer_fixture.auction_address; - let auction_custody_token_address = initial_offer_fixture.auction_custody_token_address; +) { + let test_ctx = &testing_context.test_context; + let owner_keypair = testing_context.testing_actors.owner.keypair(); + let auction_state = &mut testing_context + .testing_state + .auction_state + .get_active_auction_mut() + .unwrap(); + let auction_address = auction_state.auction_address; + let auction_custody_token_address = auction_state.auction_custody_token_address; // Decrease the offer by 0.5 usdc let improve_offer_ix = ImproveOfferIx { - offer_price: initial_offer_fixture.offer_price - 500_000, + offer_price: auction_state.best_offer.best_offer_price - 500_000, }; let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; - let transfer_authority = Pubkey::find_program_address(&[TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &improve_offer_ix.offer_price.to_be_bytes()], &program_id).0; - solver.approve_usdc(testing_context, &transfer_authority, 420_000__000_000).await; + let transfer_authority = Pubkey::find_program_address( + &[ + TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &improve_offer_ix.offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; let offer_token = solver.token_account_address().unwrap(); let active_auction = ActiveAuction { auction: auction_address, custody_token: auction_custody_token_address, config: auction_config, - best_offer_token: initial_offer_fixture.offer_token, + best_offer_token: auction_state.best_offer.best_offer_token, }; let improve_offer_accounts = ImproveOfferAccounts { transfer_authority, @@ -201,7 +311,7 @@ pub async fn improve_offer( let mut account_metas = improve_offer_accounts.to_account_metas(None); for meta in account_metas.iter_mut() { - if meta.pubkey == initial_offer_fixture.offer_token { + if meta.pubkey == auction_state.best_offer.best_offer_token { meta.is_writable = true; } } @@ -217,15 +327,18 @@ pub async fn improve_offer( &[improve_offer_ix_anchor], Some(&owner_keypair.pubkey()), &[&owner_keypair], - testing_context.borrow().last_blockhash, + test_ctx.borrow().last_blockhash, ); - - testing_context.borrow_mut().banks_client.process_transaction(tx).await.expect("Failed to improve offer"); - AuctionOfferFixture { - auction_address, - auction_custody_token_address, - offer_token, - offer_price: improve_offer_ix.offer_price, - } -} \ No newline at end of file + test_ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await + .expect("Failed to improve offer"); + + auction_state.best_offer = AuctionOffer { + best_offer_token: offer_token, + best_offer_price: improve_offer_ix.offer_price, + }; +} diff --git a/solana/programs/matching-engine/tests/utils/cctp_message.rs b/solana/programs/matching-engine/tests/utils/cctp_message.rs index 263934ac5..811e16ce0 100644 --- a/solana/programs/matching-engine/tests/utils/cctp_message.rs +++ b/solana/programs/matching-engine/tests/utils/cctp_message.rs @@ -1,18 +1,21 @@ +use crate::utils::ETHEREUM_USDC_ADDRESS; use anchor_lang::prelude::*; -use common::wormhole_cctp_solana::cctp::message_transmitter_program::MessageTransmitterConfig; +use common::wormhole_cctp_solana::cctp::TOKEN_MESSENGER_MINTER_PROGRAM_ID; +use common::wormhole_cctp_solana::cctp::{ + message_transmitter_program::MessageTransmitterConfig, + token_messenger_minter_program::RemoteTokenMessenger, +}; +use matching_engine::state::FastMarketOrder; use num_traits::FromBytes; +use ruint::Uint; +use secp256k1::SecretKey as SecpSecretKey; +use solana_program::keccak::{Hash, Hasher}; +use solana_program_test::ProgramTestContext; use solana_sdk::keccak; +use std::cell::RefCell; use std::fmt::Display; -use solana_program::keccak::{Hash, Hasher}; -use secp256k1::SecretKey as SecpSecretKey; -use std::str::FromStr; use std::rc::Rc; -use std::cell::RefCell; -use solana_program_test::ProgramTestContext; -use common::wormhole_cctp_solana::cctp::{MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID}; - - -use crate::utils::ETHEREUM_USDC_ADDRESS; +use std::str::FromStr; use super::{Chain, CHAIN_TO_DOMAIN, GUARDIAN_SECRET_KEY}; @@ -133,16 +136,12 @@ pub struct CctpRemoteTokenMessenger { pub token_messenger: Pubkey, } -impl CctpRemoteTokenMessenger { - pub fn new(domain: u32, token_messenger: Pubkey) -> Self { - Self { domain, token_messenger } - } - - pub fn try_deserialize(data: &[u8]) -> Result { - require_eq!(data.len(), 36, TokenMessengerError::MalformedMessage); - let domain = u32::from_be_bytes(data[0..4].try_into().unwrap()); - let token_messenger = Pubkey::try_from(&data[4..36]).unwrap(); - Ok(Self { domain, token_messenger }) +impl From<&RemoteTokenMessenger> for CctpRemoteTokenMessenger { + fn from(value: &RemoteTokenMessenger) -> Self { + Self { + domain: value.domain, + token_messenger: Pubkey::from(value.token_messenger), + } } } @@ -164,6 +163,7 @@ where } } +#[allow(dead_code)] impl<'a> Message<'a> { // Indices of each field in the message const VERSION_INDEX: usize = 0; @@ -177,17 +177,11 @@ impl<'a> Message<'a> { /// Validates source array size and returns a new message pub fn new(expected_version: u32, message_bytes: &'a [u8]) -> Result { - require_gte!( - message_bytes.len(), - Self::MESSAGE_BODY_INDEX - ); + require_gte!(message_bytes.len(), Self::MESSAGE_BODY_INDEX); let message = Self { data: message_bytes, }; - require_eq!( - expected_version, - message.version()?, - ); + require_eq!(expected_version, message.version()?,); Ok(message) } @@ -293,10 +287,10 @@ impl<'a> Message<'a> { /// Reads pubkey field at the given offset fn read_pubkey(&self, index: usize) -> Result { - Ok(Pubkey::try_from( - &self.data[index..checked_add(index, std::mem::size_of::())?], + Ok( + Pubkey::try_from(&self.data[index..checked_add(index, std::mem::size_of::())?]) + .map_err(|_| MessageTransmitterError::MalformedMessage)?, ) - .map_err(|_| MessageTransmitterError::MalformedMessage)?) } } @@ -316,10 +310,9 @@ impl<'a> BurnMessage<'a> { // 4 byte version + 32 bytes burnToken + 32 bytes mintRecipient + 32 bytes amount + 32 bytes messageSender const BURN_MESSAGE_LEN: usize = 132; // EVM amount is 32 bytes while we use only 8 bytes on Solana - const AMOUNT_OFFSET: usize = 24; /// Validates source array size and returns a new message - pub fn new(expected_version: u32, message_bytes: &'a [u8]) -> Result { + pub fn new(message_bytes: &'a [u8]) -> Result { require_eq!( message_bytes.len(), Self::BURN_MESSAGE_LEN, @@ -328,11 +321,7 @@ impl<'a> BurnMessage<'a> { let message = Self { data: message_bytes, }; - require_eq!( - expected_version, - message.version()?, - TokenMessengerError::InvalidMessageBodyVersion - ); + Ok(message) } @@ -342,7 +331,7 @@ impl<'a> BurnMessage<'a> { version: u32, burn_token: &Pubkey, mint_recipient: &Pubkey, - amount: u64, + amount: Uint<256, 4>, message_sender: &Pubkey, ) -> Result> { let mut output = vec![0; Self::BURN_MESSAGE_LEN]; @@ -352,19 +341,14 @@ impl<'a> BurnMessage<'a> { .copy_from_slice(burn_token.as_ref()); output[Self::MINT_RECIPIENT_INDEX..Self::AMOUNT_INDEX] .copy_from_slice(mint_recipient.as_ref()); - output[(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET)..Self::MSG_SENDER_INDEX] - .copy_from_slice(&amount.to_be_bytes()); + output[Self::AMOUNT_INDEX..Self::MSG_SENDER_INDEX] + .copy_from_slice(&amount.to_be_bytes::<32>()); output[Self::MSG_SENDER_INDEX..Self::BURN_MESSAGE_LEN] .copy_from_slice(message_sender.as_ref()); Ok(output) } - /// Returns version field - pub fn version(&self) -> Result { - self.read_integer::(Self::VERSION_INDEX) - } - /// Returns burn_token field pub fn burn_token(&self) -> Result { self.read_pubkey(Self::BURN_TOKEN_INDEX) @@ -376,14 +360,12 @@ impl<'a> BurnMessage<'a> { } /// Returns amount field - pub fn amount(&self) -> Result { - require!( - self.data[Self::AMOUNT_INDEX..(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET)] - .iter() - .all(|&x| x == 0), - TokenMessengerError::MalformedMessage - ); - self.read_integer::(Self::AMOUNT_INDEX + Self::AMOUNT_OFFSET) + pub fn amount(&self) -> Result> { + Ok(Uint::from_be_bytes::<32>( + self.data[Self::AMOUNT_INDEX..Self::AMOUNT_INDEX + 32] + .try_into() + .unwrap(), + )) } /// Returns message_sender field @@ -394,35 +376,21 @@ impl<'a> BurnMessage<'a> { //////////////////// // private helpers - /// Reads integer field at the given offset - fn read_integer(&self, index: usize) -> Result - where - T: num_traits::PrimInt + FromBytes + Display, - &'a ::Bytes: TryFrom<&'a [u8]> + 'a, - { - Ok(T::from_be_bytes( - self.data[index..checked_add(index, std::mem::size_of::())?] - .try_into() - .map_err(|_| TokenMessengerError::MalformedMessage)?, - )) - } - /// Reads pubkey field at the given offset fn read_pubkey(&self, index: usize) -> Result { - Ok(Pubkey::try_from( - &self.data[index..checked_add(index, std::mem::size_of::())?], + Ok( + Pubkey::try_from(&self.data[index..checked_add(index, std::mem::size_of::())?]) + .map_err(|_| TokenMessengerError::MalformedMessage)?, ) - .map_err(|_| TokenMessengerError::MalformedMessage)?) } } pub struct CircleAttester { - // You'll need to define a private key constant similar to GUARDIAN_KEY in TypeScript + // Default implements this to be the guardian key from file guardian_secret_key: SecpSecretKey, } impl CircleAttester { - pub fn create_attestation(&self, message: &[u8]) -> [u8; 65] { // Sign the message hash with the guardian key let secp = secp256k1::SECP256K1; @@ -434,15 +402,24 @@ impl CircleAttester { let (recovery_id, compact_sig) = recoverable_signature.serialize_compact(); // Recovery ID goes in byte 65 signature_bytes[0..64].copy_from_slice(&compact_sig); - signature_bytes[64] = i32::from(recovery_id) as u8; + let recovery_id_try = i32::from(recovery_id) as u8; + let recovery_id_true = if recovery_id_try < 27 { + recovery_id_try + 27 + } else { + recovery_id_try + }; + signature_bytes[64] = recovery_id_true; // This is only ever 0..4 signature_bytes } } impl Default for CircleAttester { fn default() -> Self { - let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); - Self { guardian_secret_key } + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY) + .expect("Failed to parse guardian secret key"); + Self { + guardian_secret_key, + } } } @@ -453,6 +430,14 @@ pub struct CctpTokenBurnMessage { pub cctp_attestation: Vec, } +impl CctpTokenBurnMessage { + pub fn verify_cctp_message(&self, fast_market_order: &FastMarketOrder) -> Result<()> { + self.cctp_message.body.verify(fast_market_order)?; + self.cctp_message.header.verify(fast_market_order)?; + Ok(()) + } +} + pub struct CctpMessageHeader { pub version: u32, pub source_domain: u32, @@ -466,49 +451,71 @@ pub struct CctpMessageHeader { impl CctpMessageHeader { pub fn encode(&self) -> Vec { let mut buf = Vec::with_capacity(116); - buf[0..4].copy_from_slice(&self.version.to_be_bytes()); - buf[4..8].copy_from_slice(&self.source_domain.to_be_bytes()); - buf[8..12].copy_from_slice(&self.destination_domain.to_be_bytes()); - buf[12..20].copy_from_slice(&self.nonce.to_be_bytes()); - buf[20..52].copy_from_slice(&self.sender); - buf[52..84].copy_from_slice(&self.recipient); - buf[84..116].copy_from_slice(&self.destination_caller); + buf.extend_from_slice(&self.version.to_be_bytes()); + buf.extend_from_slice(&self.source_domain.to_be_bytes()); + buf.extend_from_slice(&self.destination_domain.to_be_bytes()); + buf.extend_from_slice(&self.nonce.to_be_bytes()); + buf.extend_from_slice(&self.sender); + buf.extend_from_slice(&self.recipient); + buf.extend_from_slice(&self.destination_caller); + assert_eq!(buf.len(), 116, "Cctp message header length mismatch"); buf } -} + // TODO: Add actual checks or remove if not needed + pub fn verify(&self, _fast_market_order: &FastMarketOrder) -> Result<()> { + Ok(()) + } +} pub struct CctpMessageBody { pub version: u32, pub burn_token_address: [u8; 32], pub mint_recipient: [u8; 32], - pub amount: [u8; 32], // EVM amount as uint256 now in big endian byte format + pub amount: Uint<256, 4>, // EVM amount as uint256 now in big endian byte format pub message_sender: [u8; 32], } impl CctpMessageBody { pub fn encode(&self) -> Vec { let mut buf = Vec::with_capacity(132); - buf[0..4].copy_from_slice(&self.version.to_be_bytes()); - buf[4..36].copy_from_slice(&self.burn_token_address); - buf[36..68].copy_from_slice(&self.mint_recipient); - buf[68..100].copy_from_slice(&self.amount); - buf[100..132].copy_from_slice(&self.message_sender); + buf.extend_from_slice(&self.version.to_be_bytes()); + buf.extend_from_slice(&self.burn_token_address); + buf.extend_from_slice(&self.mint_recipient); + buf.extend_from_slice(&self.amount.to_be_bytes::<32>()); + buf.extend_from_slice(&self.message_sender); + assert_eq!(buf.len(), 132, "Cctp message body length mismatch"); buf } + + pub fn verify(&self, fast_market_order: &FastMarketOrder) -> Result<()> { + assert_eq!( + fast_market_order.amount_in, + self.amount.as_limbs()[0], // Since it is be encoded, the first limb will contain the u64 amount + "Cctp message amount mismatch" + ); + Ok(()) + } } impl From<&BurnMessage<'_>> for CctpMessageBody { - fn from(value: &BurnMessage) -> Self { - Self { version: value.version().expect("Version not found"), burn_token_address: value.burn_token().expect("Burn token address not found").to_bytes(), mint_recipient: value.mint_recipient().expect("Mint recipient not found").to_bytes(), amount: to_uint256_bytes(value.amount().expect("Amount not found")), message_sender: value.message_sender().expect("Message sender not found").to_bytes() } + Self { + version: 0, + burn_token_address: value + .burn_token() + .expect("Burn token address not found") + .to_bytes(), + mint_recipient: value + .mint_recipient() + .expect("Mint recipient not found") + .to_bytes(), + amount: value.amount().expect("Amount not found"), + message_sender: value + .message_sender() + .expect("Message sender not found") + .to_bytes(), + } } - -} - -fn to_uint256_bytes(amount: u64) -> [u8; 32] { - let mut buf = [0u8; 32]; - buf[32-8..].copy_from_slice(&amount.to_be_bytes()); - buf } pub struct CctpMessage { @@ -519,35 +526,41 @@ pub struct CctpMessage { impl CctpMessage { pub fn encode(&self) -> Vec { let mut buf = Vec::with_capacity(116 + 132); - buf[0..116].copy_from_slice(&self.header.encode()); - buf[116..].copy_from_slice(&self.body.encode()); + buf.extend_from_slice(&self.header.encode()); + buf.extend_from_slice(&self.body.encode()); + assert_eq!(buf.len(), 116 + 132, "Cctp message length mismatch"); buf } } - pub async fn craft_cctp_token_burn_message( test_ctx: &Rc>, source_cctp_domain: u32, cctp_nonce: u64, - amount: u64, // Only allows for 8 byte amounts for now. If we want larger amount support, we can change this to uint256. + amount: Uint<256, 4>, // Only allows for 8 byte amounts for now. If we want larger amount support, we can change this to uint256. message_transmitter_config_pubkey: &Pubkey, - remote_token_messenger_pubkey: &Pubkey, + remote_token_messenger: &CctpRemoteTokenMessenger, cctp_mint_recipient: &Pubkey, custodian_address: &Pubkey, ) -> Result { - let destination_cctp_domain = CHAIN_TO_DOMAIN[Chain::Solana as usize].1; // Hard code solana as destination domain + let destination_cctp_domain = CHAIN_TO_DOMAIN[Chain::Solana as usize].1; // Hard code solana as destination domain assert_eq!(destination_cctp_domain, 5); - let message_transmitter_config_data = test_ctx.borrow_mut().banks_client.get_account(*message_transmitter_config_pubkey).await.expect("Failed to fetch account").expect("Account not found").data; - let message_transmitter_config = MessageTransmitterConfig::try_deserialize(&mut &message_transmitter_config_data[..]).expect("Failed to deserialize message transmitter config"); + let message_transmitter_config_data = test_ctx + .borrow_mut() + .banks_client + .get_account(*message_transmitter_config_pubkey) + .await + .expect("Failed to fetch account") + .expect("Account not found") + .data; + let message_transmitter_config = + MessageTransmitterConfig::try_deserialize(&mut &message_transmitter_config_data[..]) + .expect("Failed to deserialize message transmitter config"); let cctp_header_version = message_transmitter_config.version; let local_domain = message_transmitter_config.local_domain; assert_eq!(local_domain, destination_cctp_domain); - let remote_token_messenger_data = test_ctx.borrow_mut().banks_client.get_account(*remote_token_messenger_pubkey).await.expect("Failed to fetch account").expect("Account not found").data; - let remote_token_messenger = CctpRemoteTokenMessenger::try_deserialize(&mut &remote_token_messenger_data[..]).expect("Could not deserialize remote token messenger"); let source_token_messenger = remote_token_messenger.token_messenger; let burn_token_address = ethereum_address_to_universal(ETHEREUM_USDC_ADDRESS); - let burn_message_vec = BurnMessage::format_message( 0, &Pubkey::try_from_slice(&burn_token_address).unwrap(), @@ -556,7 +569,7 @@ pub async fn craft_cctp_token_burn_message( &Pubkey::try_from_slice(&[0u8; 32]).unwrap(), )?; - let burn_message = BurnMessage::new(0, &burn_message_vec).unwrap(); + let burn_message = BurnMessage::new(&burn_message_vec).unwrap(); let cctp_message_body = CctpMessageBody::from(&burn_message); @@ -569,9 +582,16 @@ pub async fn craft_cctp_token_burn_message( recipient: TOKEN_MESSENGER_MINTER_PROGRAM_ID.to_bytes(), destination_caller: custodian_address.to_bytes(), }; - - assert_eq!(cctp_message_body.encode().len(), burn_message_vec.len(), "CCTP message body length mismatch"); - assert_eq!(cctp_message_body.encode(), burn_message_vec, "CCTP message body mismatch"); + assert_eq!( + cctp_message_body.encode().len(), + burn_message_vec.len(), + "CCTP message body length mismatch" + ); + assert_eq!( + cctp_message_body.encode(), + burn_message_vec, + "CCTP message body mismatch" + ); let cctp_message = CctpMessage { header: cctp_message_header, @@ -581,7 +601,7 @@ pub async fn craft_cctp_token_burn_message( let encoded_cctp_message = cctp_message.encode(); let cctp_attestation = CircleAttester::default().create_attestation(&encoded_cctp_message); - + Ok(CctpTokenBurnMessage { destination_cctp_domain, cctp_message, @@ -593,15 +613,15 @@ pub async fn craft_cctp_token_burn_message( pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { // Remove '0x' prefix if present let address_str = eth_address.strip_prefix("0x").unwrap_or(eth_address); - + // Decode the hex string to bytes let mut address_bytes = [0u8; 20]; // Ethereum addresses are 20 bytes hex::decode_to_slice(address_str, &mut address_bytes as &mut [u8]) .expect("Invalid Ethereum address format"); - + // Create a 32-byte array with leading zeros (Ethereum addresses are padded with zeros on the left) let mut universal_address = [0u8; 32]; universal_address[12..32].copy_from_slice(&address_bytes); - + universal_address -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index ec6cb4913..89ef48699 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] +use solana_program::pubkey; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; -use solana_program::pubkey; // Program IDs cfg_if::cfg_if! { @@ -56,14 +56,21 @@ cfg_if::cfg_if! { } } -pub const GUARDIAN_SECRET_KEY: &str = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; -pub const TOKEN_ROUTER_PID: Pubkey = solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); -pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); -pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); -pub const WORMHOLE_POST_MESSAGE_SHIM_PID: Pubkey = pubkey!("EtZMZM22ViKMo4r5y4Anovs3wKQ2owUmDpjygnMMcdEX"); -pub const WORMHOLE_VERIFY_VAA_SHIM_PID: Pubkey = pubkey!("EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at"); -pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY: Pubkey = pubkey!("HQS31aApX3DDkuXgSpV9XyDUNtFgQ31pUn5BNWHG2PSp"); -pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY_BUMP: u8 = 255; +pub const GUARDIAN_SECRET_KEY: &str = + "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; +pub const TOKEN_ROUTER_PID: Pubkey = + solana_program::pubkey!("tD8RmtdcV7bzBeuFgyrFc8wvayj988ChccEzRQzo6md"); +pub const CCTP_TOKEN_MESSENGER_MINTER_PID: Pubkey = + solana_program::pubkey!("CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"); +pub const CCTP_MESSAGE_TRANSMITTER_PID: Pubkey = + solana_program::pubkey!("CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"); +pub const WORMHOLE_POST_MESSAGE_SHIM_PID: Pubkey = + pubkey!("EtZMZM22ViKMo4r5y4Anovs3wKQ2owUmDpjygnMMcdEX"); +pub const WORMHOLE_VERIFY_VAA_SHIM_PID: Pubkey = + pubkey!("EFaNWErqAtVWufdNb7yofSHHfWFos843DFpu4JBw24at"); +pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY: Pubkey = + pubkey!("HQS31aApX3DDkuXgSpV9XyDUNtFgQ31pUn5BNWHG2PSp"); +pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY_BUMP: u8 = 255; /// Keypairs as base64 strings (taken from consts.ts in ts tests) // pub const PAYER_KEYPAIR_B64: &str = "cDfpY+VbRFXPPwouZwAx+ha9HqedkhqUr5vUaFa2ucAMGliG/hCT35/EOMKW+fcnW3cYtrwOFW2NM2xY8IOZbQ=="; @@ -73,13 +80,17 @@ pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY_BUMP: u8 = 255; /// Keypairs as base58 strings (taken from consts.ts in ts tests using a converter) #[allow(dead_code)] -pub const PAYER_KEYPAIR_B58: &str = "4NMwxzmYj2uvHuq8xoqhY8RXg0Pd5zkvmfWAL6YvbYFuViXVCBDK5Pru9GgqEVEZo6UXcPVH6rdR8JKgKxHGkXDp"; +pub const PAYER_KEYPAIR_B58: &str = + "4NMwxzmYj2uvHuq8xoqhY8RXg0Pd5zkvmfWAL6YvbYFuViXVCBDK5Pru9GgqEVEZo6UXcPVH6rdR8JKgKxHGkXDp"; #[allow(dead_code)] -pub const OWNER_ASSISTANT_KEYPAIR_B58: &str = "2UbUgoidcNHxVEDG6ADNKGaGDqBTXTVw6B9pWvJtLNhbxcQDkdeEyBYBYYYxxDy92ckXUEaU9chWEGi5jc8Uc9e3"; +pub const OWNER_ASSISTANT_KEYPAIR_B58: &str = + "2UbUgoidcNHxVEDG6ADNKGaGDqBTXTVw6B9pWvJtLNhbxcQDkdeEyBYBYYYxxDy92ckXUEaU9chWEGi5jc8Uc9e3"; #[allow(dead_code)] -pub const OWNER_KEYPAIR_B58: &str = "3M5rkG5DQVEGQFRtA1qruxPqJvYBbkGCdkCdB9ZjcnQnYL9ec8W78pLcQHVtjJzHP8phUXQ8V1SXbgZK9ZaFaS6U"; +pub const OWNER_KEYPAIR_B58: &str = + "3M5rkG5DQVEGQFRtA1qruxPqJvYBbkGCdkCdB9ZjcnQnYL9ec8W78pLcQHVtjJzHP8phUXQ8V1SXbgZK9ZaFaS6U"; #[allow(dead_code)] -pub const PLAYER_ONE_KEYPAIR_B58: &str = "yqJrKqGqzuW6nEmfj62AgvZWqgGv9TqxfvPXiGvf8DxGDWz3UNkQdDfKDnBYpHQxPRVrYMupDKqbGVYHhfZApGb"; +pub const PLAYER_ONE_KEYPAIR_B58: &str = + "yqJrKqGqzuW6nEmfj62AgvZWqgGv9TqxfvPXiGvf8DxGDWz3UNkQdDfKDnBYpHQxPRVrYMupDKqbGVYHhfZApGb"; // Helper functions to get keypairs #[allow(dead_code)] @@ -105,7 +116,8 @@ pub fn get_player_one_keypair() -> Keypair { // TODO: Remove these constants if not ever used // Other constants #[allow(dead_code)] -pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = solana_program::pubkey!("11111111111111111111111111111115"); +pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = + solana_program::pubkey!("11111111111111111111111111111115"); pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; @@ -160,4 +172,3 @@ impl Chain { } } } - diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/utils/initialize.rs index 1bff89411..91eacd5a9 100644 --- a/solana/programs/matching-engine/tests/utils/initialize.rs +++ b/solana/programs/matching-engine/tests/utils/initialize.rs @@ -1,25 +1,24 @@ use solana_program_test::ProgramTestContext; use solana_sdk::{ - instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::{Transaction, VersionedTransaction} + instruction::Instruction, + pubkey::Pubkey, + signature::Signer, + transaction::{Transaction, VersionedTransaction}, }; -use std::rc::Rc; use std::cell::RefCell; +use std::rc::Rc; -use solana_program::{bpf_loader_upgradeable, system_program}; -use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; use anchor_lang::AccountDeserialize; +use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; +use solana_program::{bpf_loader_upgradeable, system_program}; +use super::super::TestingContext; use anchor_lang::{InstructionData, ToAccountMetas}; use matching_engine::{ accounts::Initialize, + state::{AuctionConfig, AuctionParameters, Custodian}, InitializeArgs, - state::{ - AuctionParameters, - Custodian, - AuctionConfig - }, }; -use super::super::TestingContext; pub struct InitializeAddresses { pub custodian_address: Pubkey, @@ -71,7 +70,12 @@ impl From<&Custodian> for TestCustodian { } impl InitializeFixture { - pub fn verify_custodian(&self, owner: Pubkey, owner_assistant: Pubkey, fee_recipient_token: Pubkey) { + pub fn verify_custodian( + &self, + owner: Pubkey, + owner_assistant: Pubkey, + fee_recipient_token: Pubkey, + ) { let expected_custodian = TestCustodian { owner, pending_owner: None, @@ -88,26 +92,23 @@ impl InitializeFixture { } } -pub async fn initialize_program(testing_context: &TestingContext, program_id: Pubkey, usdc_mint_address: Pubkey, cctp_mint_recipient: Pubkey) -> InitializeFixture { +pub async fn initialize_program(testing_context: &TestingContext) -> InitializeFixture { let test_context = testing_context.test_context.clone(); - - let (custodian, _custodian_bump) = Pubkey::find_program_address( - &[Custodian::SEED_PREFIX], - &program_id, - ); + let program_id = testing_context.get_matching_engine_program_id(); + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); + let (custodian, _custodian_bump) = + Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); let (auction_config, _auction_config_bump) = Pubkey::find_program_address( - &[ - AuctionConfig::SEED_PREFIX, - &0u32.to_be_bytes(), - ], + &[AuctionConfig::SEED_PREFIX, &0u32.to_be_bytes()], &program_id, ); - + // Create AuctionParameters let auction_params = AuctionParameters { user_penalty_reward_bps: 250_000, // 25% - initial_penalty_bps: 250_000, // 25% + initial_penalty_bps: 250_000, // 25% duration: 2, grace_period: 5, penalty_period: 10, @@ -118,11 +119,9 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu // Create the instruction data let ix_data = matching_engine::instruction::Initialize { - args: InitializeArgs { - auction_params, - }, + args: InitializeArgs { auction_params }, }; - + // Get account metas let accounts = Initialize { owner: testing_context.testing_actors.owner.pubkey(), @@ -130,9 +129,15 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu auction_config, owner_assistant: testing_context.testing_actors.owner_assistant.pubkey(), fee_recipient: testing_context.testing_actors.fee_recipient.pubkey(), - fee_recipient_token: testing_context.testing_actors.fee_recipient.token_account_address().unwrap(), + fee_recipient_token: testing_context + .testing_actors + .fee_recipient + .token_account_address() + .unwrap(), cctp_mint_recipient: cctp_mint_recipient, - usdc: matching_engine::accounts::Usdc{mint: usdc_mint_address}, + usdc: matching_engine::accounts::Usdc { + mint: usdc_mint_address, + }, program_data: testing_context.program_data_account, upgrade_manager_authority: common::UPGRADE_MANAGER_AUTHORITY, upgrade_manager_program: common::UPGRADE_MANAGER_PROGRAM_ID, @@ -141,7 +146,7 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu token_program: spl_token::id(), associated_token_program: spl_associated_token_account::id(), }; - + // Create the instruction let instruction = Instruction { program_id: program_id, @@ -149,29 +154,50 @@ pub async fn initialize_program(testing_context: &TestingContext, program_id: Pu data: ix_data.data(), }; // Create and sign transaction - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&test_context.borrow().payer.pubkey()), + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); + let new_blockhash = test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + transaction.sign( + &[ + &test_context.borrow().payer, + &testing_context.testing_actors.owner.keypair(), + ], + new_blockhash, ); - let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - transaction.sign(&[&test_context.borrow().payer, &testing_context.testing_actors.owner.keypair()], new_blockhash); // Process transaction - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .unwrap(); // Verify the results - let custodian_account = test_context.borrow_mut().banks_client + let custodian_account = test_context + .borrow_mut() + .banks_client .get_account(custodian.clone()) .await .unwrap() .unwrap(); - - let custodian_data = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + + let custodian_data = + Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); let initialize_addresses = InitializeAddresses { custodian_address: custodian, auction_config_address: auction_config, cctp_mint_recipient: cctp_mint_recipient, }; - InitializeFixture { test_context, custodian: custodian_data, addresses: initialize_addresses } -} \ No newline at end of file + InitializeFixture { + test_context, + custodian: custodian_data, + addresses: initialize_addresses, + } +} diff --git a/solana/programs/matching-engine/tests/utils/lookup_table.rs b/solana/programs/matching-engine/tests/utils/lookup_table.rs deleted file mode 100644 index e22c95853..000000000 --- a/solana/programs/matching-engine/tests/utils/lookup_table.rs +++ /dev/null @@ -1,113 +0,0 @@ -use solana_program::address_lookup_table::{ - instruction::{create_lookup_table, extend_lookup_table}, - state::AddressLookupTable, -}; -use solana_program_test::ProgramTestContext; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, transaction::Transaction}; -use solana_program::pubkey; -use std::rc::Rc; -use std::cell::RefCell; - -// TODO: Figure out each of these addresses ... -struct LookupTableAddresses { - pub core_bridge_config: Pubkey, - pub core_emitter_sequence: Pubkey, // Nothing needs to be done: will be created from first message by bridge - pub core_fee_collector: Pubkey, - pub core_bridge_program: Pubkey, // ?? - pub matching_engine_program: Pubkey, // Will need to be loaded as a .so - pub system_program: Pubkey, - pub rent: Pubkey, - pub clock: Pubkey, - pub custodian: Pubkey, - pub event_authority: Pubkey, // Derive key for it - pub cctp_mint_recipient: Pubkey, // Initialised from Initialize - pub token_messenger: Pubkey, - pub token_minter: Pubkey, - pub token_messenger_minter_sender_authority: Pubkey, - pub token_messenger_minter_program: Pubkey, - pub message_transmitter_authority: Pubkey, - pub message_transmitter_config: Pubkey, - pub message_transmitter_program: Pubkey, - pub token_program: Pubkey, - pub mint: Pubkey, // (USDC mint address) - pub local_token: Pubkey, - pub token_messenger_minter_custody_token: Pubkey, // usdc custody token - pub token_messenger_minter_event_authority: Pubkey, // Derive key for it - pub message_transmitter_event_authority: Pubkey, // Derive key for it -} - -impl LookupTableAddresses { - pub fn new(matching_engine_program: Pubkey, custodian: Pubkey, cctp_mint_recipient: Pubkey, mint: Pubkey) -> Self { - Self { - core_bridge_config: pubkey!(""), - core_emitter_sequence: pubkey!(""), - core_fee_collector: pubkey!(""), - core_bridge_program: pubkey!(""), - matching_engine_program, - system_program: solana_program::system_program::ID, - rent: solana_program::sysvar::rent::ID, - clock: solana_program::sysvar::clock::ID, - custodian, - event_authority: pubkey!(""), - cctp_mint_recipient, - token_messenger: pubkey!(""), - token_minter: pubkey!(""), - token_messenger_minter_sender_authority: pubkey!(""), - token_messenger_minter_program: pubkey!(""), - message_transmitter_authority: pubkey!(""), - message_transmitter_config: pubkey!(""), - message_transmitter_program: pubkey!(""), - token_program: anchor_spl::token::ID, - mint, - local_token: pubkey!(""), - token_messenger_minter_custody_token: pubkey!(""), - token_messenger_minter_event_authority: pubkey!(""), - message_transmitter_event_authority: pubkey!(""), - } - } -} - -async fn setup_lookup_table( - test_context: &Rc>, - addresses: Vec, -) -> Pubkey { - let mut ctx = test_context.borrow_mut(); - - // Get recent slot - let slot = ctx.banks_client.get_root_slot().await.unwrap(); - - // Create lookup table - let (lookup_table_address, create_ix) = create_lookup_table( - ctx.payer.pubkey(), // Authority - ctx.payer.pubkey(), // Payer - slot, // Recent slot - ); - - // Process create instruction - let create_tx = Transaction::new_signed_with_payer( - &[create_ix], - Some(&ctx.payer.pubkey()), - &[&ctx.payer], - ctx.last_blockhash, - ); - ctx.banks_client.process_transaction(create_tx).await.unwrap(); - - // Extend lookup table with addresses - let extend_ix = extend_lookup_table( - lookup_table_address, - ctx.payer.pubkey(), // Authority - Some(ctx.payer.pubkey()), // Payer (optional) - addresses, - ); - - // Process extend instruction - let extend_tx = Transaction::new_signed_with_payer( - &[extend_ix], - Some(&ctx.payer.pubkey()), - &[&ctx.payer], - ctx.last_blockhash, - ); - ctx.banks_client.process_transaction(extend_tx).await.unwrap(); - - lookup_table_address -} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/mint.rs b/solana/programs/matching-engine/tests/utils/mint.rs index 011bcf4de..5b0e4a199 100644 --- a/solana/programs/matching-engine/tests/utils/mint.rs +++ b/solana/programs/matching-engine/tests/utils/mint.rs @@ -1,12 +1,12 @@ +use anchor_spl::token::spl_token; +use solana_cli_output::CliAccount; +use solana_program_test::ProgramTestContext; use solana_sdk::{ account::{AccountSharedData, ReadableAccount, WritableAccount}, program_pack::Pack, - signer::Signer, pubkey::Pubkey, + signer::Signer, }; -use solana_program_test::ProgramTestContext; -use solana_cli_output::CliAccount; -use anchor_spl::token::spl_token; use spl_token::state::Mint; use std::{cell::RefCell, fs::File, io::Read, path::PathBuf, rc::Rc, str::FromStr}; @@ -20,7 +20,6 @@ pub struct MintFixture { } impl MintFixture { - /// Creates a new MintFixture from a file /// /// # Arguments @@ -29,7 +28,7 @@ impl MintFixture { /// * `relative_path` - The relative path to the mint file /// /// # Returns - /// + /// /// A new MintFixture pub fn new_from_file( ctx: &Rc>, @@ -75,4 +74,4 @@ impl MintFixture { token_program: account_info.owner().to_owned(), } } -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index b354275cc..9599caf9c 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -1,21 +1,17 @@ -/// Common functions for the matching engine program tests. -/// - -pub mod token_account; -pub mod mint; -pub mod program_fixtures; +pub mod account_fixtures; pub mod airdrop; +pub mod auction; +pub mod cctp_message; pub mod constants; -// pub mod lookup_table; -// pub mod transfer_ownership; -pub mod account_fixtures; pub mod initialize; +pub mod mint; +pub mod program_fixtures; pub mod router; -pub mod vaa; -pub mod auction; +pub mod settle_auction; pub mod setup; pub mod shims; pub mod shims_execute_order; pub mod shims_prepare_order_response; -pub mod cctp_message; +pub mod token_account; +pub mod vaa; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/program_fixtures.rs b/solana/programs/matching-engine/tests/utils/program_fixtures.rs index 66a2d05e1..6cd66be71 100644 --- a/solana/programs/matching-engine/tests/utils/program_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/program_fixtures.rs @@ -1,8 +1,12 @@ +use solana_program::bpf_loader_upgradeable; use solana_program_test::ProgramTest; use solana_sdk::pubkey::Pubkey; -use solana_program::bpf_loader_upgradeable; -use super::{TOKEN_ROUTER_PID, CORE_BRIDGE_PID, CORE_BRIDGE_CONFIG, CCTP_TOKEN_MESSENGER_MINTER_PID, CCTP_MESSAGE_TRANSMITTER_PID, WORMHOLE_POST_MESSAGE_SHIM_PID, WORMHOLE_VERIFY_VAA_SHIM_PID}; +use super::{ + CCTP_MESSAGE_TRANSMITTER_PID, CCTP_TOKEN_MESSENGER_MINTER_PID, CORE_BRIDGE_CONFIG, + CORE_BRIDGE_PID, TOKEN_ROUTER_PID, WORMHOLE_POST_MESSAGE_SHIM_PID, + WORMHOLE_VERIFY_VAA_SHIM_PID, +}; fn get_program_data(owner: Pubkey) -> Vec { let state = solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { @@ -13,13 +17,15 @@ fn get_program_data(owner: Pubkey) -> Vec { } /// Initialise the upgrade manager program -/// +/// /// Returns the program data pubkey -pub fn initialise_upgrade_manager(program_test: &mut ProgramTest, program_id: &Pubkey, owner_pubkey: Pubkey) -> Pubkey { - let program_data_pubkey = Pubkey::find_program_address( - &[program_id.as_ref()], - &bpf_loader_upgradeable::id(), - ).0; +pub fn initialise_upgrade_manager( + program_test: &mut ProgramTest, + program_id: &Pubkey, + owner_pubkey: Pubkey, +) -> Pubkey { + let program_data_pubkey = + Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id()).0; // Add the program data to the program test // Compute lamports from length of program data @@ -91,4 +97,4 @@ pub fn initialise_verify_shims(program_test: &mut ProgramTest) { "AwAAABMAAABYzDrlwJeyE848gZeeG5+VcHRqpf9suVJYm96GLCXvQ5ITL7nUpCFXEU3oRgGTvfOi/PgfhqCXZfR2L9EQegCGsy16CXeSaiBRMdhzHTnL64yCsv2C+u0nEdWa8PJJnRbnJvayEbOXVsBCRBvm2GULabVOvnFeI0NUzltNNI+3S5WOiWbi7D29SVinzRXnyvB8Tj3I58Rp+SyM2I+4AFogdKO/kTlT1pUmDYi8GqJaTu42PvAACsAHZyezX76i2sKP7lzLD+p2jq9FztE2udniSQNGSuiJ9cinI/wU+TEkt8c4hDy7iehkyGLDjN3Mz5XSzDek3ANqjSMrSPYs3UcxQS9IkNp5j2iWozMfZLSMEtHVf9nL5wgRcaob4dNsr+OGeRD5nAnjR4mcGcOBkrbnOHzNdoJ3wX2rG3pQJ8CzzxeOIa0ud64GcRVJz7sfnHqdgJboXhSH81UV0CqSdTUEqNdUcbn0nttvvryJj0A+R3PpX+sV6Ayamcg0jUA8xWP46h9m", ); program_test.prefer_bpf(true); -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index f9b0266c6..17e2fb5cb 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -1,25 +1,30 @@ // Add methods for adding endpoints to the program test +use super::constants::*; +use super::setup::TestingContext; +use super::token_account::create_token_account_for_pda; use anchor_lang::prelude::*; use anchor_lang::Discriminator; use anchor_lang::{InstructionData, ToAccountMetas}; use common::wormhole_cctp_solana::cctp::token_messenger_minter_program::RemoteTokenMessenger; +use matching_engine::accounts::{ + AddCctpRouterEndpoint as AddCctpRouterEndpointAccounts, + AddLocalRouterEndpoint as AddLocalRouterEndpointAccounts, Admin, CheckedCustodian, + LocalTokenRouter, +}; +use matching_engine::instruction::{AddCctpRouterEndpoint, AddLocalRouterEndpoint}; use matching_engine::state::Custodian; use matching_engine::state::EndpointInfo; +use matching_engine::state::RouterEndpoint; +use matching_engine::AddCctpRouterEndpointArgs; use matching_engine::LOCAL_CUSTODY_TOKEN_SEED_PREFIX; use solana_program_test::ProgramTestContext; -use solana_sdk::transaction::VersionedTransaction; -use std::rc::Rc; -use std::cell::RefCell; -use matching_engine::instruction::{AddCctpRouterEndpoint, AddLocalRouterEndpoint}; -use matching_engine::accounts::{AddCctpRouterEndpoint as AddCctpRouterEndpointAccounts, AddLocalRouterEndpoint as AddLocalRouterEndpointAccounts, Admin, CheckedCustodian, LocalTokenRouter}; -use matching_engine::AddCctpRouterEndpointArgs; use solana_sdk::instruction::Instruction; -use solana_sdk::signature::{Signer, Keypair}; +use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; -use matching_engine::state::RouterEndpoint; -use super::constants::*; -use super::token_account::create_token_account_for_pda; +use solana_sdk::transaction::VersionedTransaction; +use std::cell::RefCell; +use std::rc::Rc; fn generate_admin(owner_or_assistant: Pubkey, custodian: Pubkey) -> Admin { let checked_custodian = CheckedCustodian { custodian }; @@ -35,7 +40,8 @@ async fn print_account_discriminator( address: &Pubkey, ) { println!("Printing account discriminator for address: {:?}", address); - let account = test_context.borrow_mut() + let account = test_context + .borrow_mut() .banks_client .get_account(*address) .await @@ -43,18 +49,17 @@ async fn print_account_discriminator( .expect("Account not found"); println!("Account data: {:?}", account.data); - + let account_owner = account.owner; println!("Account owner: {:?}", account_owner); // Get first 8 bytes (discriminator) let discriminator = &account.data[..8]; println!("Account discriminator: {:?}", discriminator); - + // Compare with expected discriminator (WARNING: ASSUMPTION) let expected = RemoteTokenMessenger::discriminator(); println!("Expected discriminator: {:?}", expected); - } /// A struct representing an endpoint info for testing purposes @@ -68,16 +73,36 @@ pub struct TestEndpointInfo { impl From<&EndpointInfo> for TestEndpointInfo { fn from(endpoint_info: &EndpointInfo) -> Self { - Self { chain: endpoint_info.chain, address: endpoint_info.address, mint_recipient: endpoint_info.mint_recipient, protocol: endpoint_info.protocol } + Self { + chain: endpoint_info.chain, + address: endpoint_info.address, + mint_recipient: endpoint_info.mint_recipient, + protocol: endpoint_info.protocol, + } } } impl TestEndpointInfo { - pub fn new(chain: Chain, address: &Pubkey, mint_recipient: Option<&Pubkey>, protocol: matching_engine::state::MessageProtocol) -> Self { + pub fn new( + chain: Chain, + address: &Pubkey, + mint_recipient: Option<&Pubkey>, + protocol: matching_engine::state::MessageProtocol, + ) -> Self { if let Some(mint_recipient) = mint_recipient { - Self { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: mint_recipient.to_bytes(), protocol: protocol } + Self { + chain: chain.to_chain_id(), + address: address.to_bytes(), + mint_recipient: mint_recipient.to_bytes(), + protocol: protocol, + } } else { - Self { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: address.to_bytes(), protocol: protocol } + Self { + chain: chain.to_chain_id(), + address: address.to_bytes(), + mint_recipient: address.to_bytes(), + protocol: protocol, + } } } } @@ -89,8 +114,16 @@ pub struct TestRouterEndpoints { } impl TestRouterEndpoints { - pub fn new(arbitrum: TestRouterEndpoint, ethereum: TestRouterEndpoint, solana: TestRouterEndpoint) -> Self { - Self { arbitrum, ethereum, solana } + pub fn new( + arbitrum: TestRouterEndpoint, + ethereum: TestRouterEndpoint, + solana: TestRouterEndpoint, + ) -> Self { + Self { + arbitrum, + ethereum, + solana, + } } #[allow(dead_code)] @@ -124,19 +157,30 @@ pub struct TestRouterEndpoint { impl From<(&RouterEndpoint, Pubkey)> for TestRouterEndpoint { fn from((router_endpoint, endpoint_address): (&RouterEndpoint, Pubkey)) -> Self { - Self { endpoint_address, bump: router_endpoint.bump, info: (&router_endpoint.info).into() } + Self { + endpoint_address, + bump: router_endpoint.bump, + info: (&router_endpoint.info).into(), + } } } impl TestRouterEndpoint { - pub fn verify_endpoint_info(&self, chain: Chain, address: &Pubkey, mint_recipient: Option<&Pubkey>, protocol: matching_engine::state::MessageProtocol) { + pub fn verify_endpoint_info( + &self, + chain: Chain, + address: &Pubkey, + mint_recipient: Option<&Pubkey>, + protocol: matching_engine::state::MessageProtocol, + ) { let expected_info = TestEndpointInfo::new(chain, address, mint_recipient, protocol); assert_eq!(self.info, expected_info); } } pub fn get_router_endpoint_address(program_id: Pubkey, encoded_chain: &[u8; 2]) -> Pubkey { - let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, encoded_chain], &program_id); + let (router_endpoint_address, _bump) = + Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, encoded_chain], &program_id); router_endpoint_address } @@ -151,13 +195,19 @@ pub async fn add_cctp_router_endpoint_ix( chain: Chain, ) -> TestRouterEndpoint { let admin = generate_admin(admin_owner_or_assistant, admin_custodian); - let usdc = matching_engine::accounts::Usdc{mint: usdc_mint_address}; - + let usdc = matching_engine::accounts::Usdc { + mint: usdc_mint_address, + }; + let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); let router_endpoint_address = get_router_endpoint_address(program_id, &encoded_chain); - - let local_custody_token_address = Pubkey::find_program_address(&[LOCAL_CUSTODY_TOKEN_SEED_PREFIX, &encoded_chain], &program_id).0; - + + let local_custody_token_address = Pubkey::find_program_address( + &[LOCAL_CUSTODY_TOKEN_SEED_PREFIX, &encoded_chain], + &program_id, + ) + .0; + let accounts = AddCctpRouterEndpointAccounts { payer: test_context.borrow().payer.pubkey(), admin, @@ -169,7 +219,10 @@ pub async fn add_cctp_router_endpoint_ix( system_program: anchor_lang::system_program::ID, }; - let registered_token_router_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().expect("Failed to convert registered token router address to bytes [u8; 32]"); + let registered_token_router_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&chain] + .clone() + .try_into() + .expect("Failed to convert registered token router address to bytes [u8; 32]"); let ix_data = AddCctpRouterEndpoint { args: AddCctpRouterEndpointArgs { chain: chain.to_chain_id(), @@ -177,7 +230,8 @@ pub async fn add_cctp_router_endpoint_ix( address: registered_token_router_address, mint_recipient: None, }, - }.data(); + } + .data(); let instruction = Instruction { program_id: program_id, @@ -185,43 +239,67 @@ pub async fn add_cctp_router_endpoint_ix( data: ix_data, }; - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&test_context.borrow().payer.pubkey()), - ); + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); // TODO: Figure out who the signers are - let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - transaction.sign(&[&test_context.borrow().payer, &admin_keypair], new_blockhash); + let new_blockhash = test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + transaction.sign( + &[&test_context.borrow().payer, &admin_keypair], + new_blockhash, + ); - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .unwrap(); - let endpoint_account = test_context.borrow_mut().banks_client + let endpoint_account = test_context + .borrow_mut() + .banks_client .get_account(router_endpoint_address) .await .unwrap() .unwrap(); - let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); + let endpoint_data = + RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); let test_router_endpoint = TestRouterEndpoint::from((&endpoint_data, router_endpoint_address)); - test_router_endpoint.verify_endpoint_info(chain, &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { domain: CHAIN_TO_DOMAIN[chain as usize].1 }); + test_router_endpoint.verify_endpoint_info( + chain, + &Pubkey::new_from_array(registered_token_router_address), + None, + matching_engine::state::MessageProtocol::Cctp { + domain: CHAIN_TO_DOMAIN[chain as usize].1, + }, + ); test_router_endpoint } pub async fn add_local_router_endpoint_ix( - test_context: &Rc>, + testing_context: &TestingContext, admin_owner_or_assistant: Pubkey, admin_custodian: Pubkey, admin_keypair: &Keypair, - program_id: Pubkey, - usdc_mint_address: &Pubkey, ) -> TestRouterEndpoint { + let test_context = &testing_context.test_context; + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let program_id = testing_context.get_matching_engine_program_id(); let admin = generate_admin(admin_owner_or_assistant, admin_custodian); - + let token_router_program = TOKEN_ROUTER_PID; - let token_router_emitter = Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &token_router_program).0; - let token_router_mint_recipient = create_token_account_for_pda(test_context, &token_router_emitter, usdc_mint_address).await; + let token_router_emitter = + Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &token_router_program).0; + let token_router_mint_recipient = + create_token_account_for_pda(test_context, &token_router_emitter, &usdc_mint_address).await; // Create the local token router let local_token_router = LocalTokenRouter { token_router_program, @@ -230,7 +308,8 @@ pub async fn add_local_router_endpoint_ix( }; let chain = Chain::Solana; let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); - let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); + let (router_endpoint_address, _bump) = + Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); // Create the router endpoint let accounts = AddLocalRouterEndpointAccounts { @@ -249,39 +328,61 @@ pub async fn add_local_router_endpoint_ix( data: ix_data, }; - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&test_context.borrow().payer.pubkey()), + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); + let new_blockhash = test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + transaction.sign( + &[&test_context.borrow().payer, &admin_keypair], + new_blockhash, ); - let new_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - transaction.sign(&[&test_context.borrow().payer, &admin_keypair], new_blockhash); - let versioned_transaction = VersionedTransaction::try_from(transaction).expect("Failed to convert transaction to versioned transaction"); - test_context.borrow_mut().banks_client.process_transaction(versioned_transaction).await.unwrap(); - - let endpoint_account = test_context.borrow_mut().banks_client + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await + .unwrap(); + + let endpoint_account = test_context + .borrow_mut() + .banks_client .get_account(router_endpoint_address) .await .unwrap() .unwrap(); - let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); + let endpoint_data = + RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); let test_router_endpoint = TestRouterEndpoint::from((&endpoint_data, router_endpoint_address)); - test_router_endpoint.verify_endpoint_info(chain, &token_router_emitter, Some(&token_router_mint_recipient), matching_engine::state::MessageProtocol::Local { program_id: token_router_program }); + test_router_endpoint.verify_endpoint_info( + chain, + &token_router_emitter, + Some(&token_router_mint_recipient), + matching_engine::state::MessageProtocol::Local { + program_id: token_router_program, + }, + ); test_router_endpoint } pub async fn create_cctp_router_endpoints_test( - test_context: &Rc>, + testing_context: &TestingContext, admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, - arb_remote_token_messenger: Pubkey, - eth_remote_token_messenger: Pubkey, - usdc_mint_address: Pubkey, admin_keypair: Rc, - program_id: Pubkey, ) -> [TestRouterEndpoint; 2] { + let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let program_id = testing_context.get_matching_engine_program_id(); + let arb_remote_token_messenger = fixture_accounts.arbitrum_remote_token_messenger; + let test_context = &testing_context.test_context; let arb_chain = Chain::Arbitrum; let arbitrum_token_router_endpoint = add_cctp_router_endpoint_ix( test_context, @@ -292,9 +393,11 @@ pub async fn create_cctp_router_endpoints_test( arb_remote_token_messenger, usdc_mint_address, arb_chain, - ).await; + ) + .await; let eth_chain = Chain::Ethereum; + let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; let ethereum_token_router_endpoint = add_cctp_router_endpoint_ix( test_context, admin_owner_or_assistant, @@ -304,39 +407,57 @@ pub async fn create_cctp_router_endpoints_test( eth_remote_token_messenger, usdc_mint_address, eth_chain, - ).await; + ) + .await; - [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] + [ + arbitrum_token_router_endpoint, + ethereum_token_router_endpoint, + ] } pub async fn create_all_router_endpoints_test( - test_context: &Rc>, + testing_context: &TestingContext, admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, - arb_remote_token_messenger: Pubkey, - eth_remote_token_messenger: Pubkey, - usdc_mint_address: Pubkey, admin_keypair: Rc, - program_id: Pubkey, ) -> TestRouterEndpoints { - let [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] = create_cctp_router_endpoints_test( - test_context, - admin_owner_or_assistant.clone(), - custodian_address.clone(), - arb_remote_token_messenger, - eth_remote_token_messenger, - usdc_mint_address, - admin_keypair.clone(), - program_id, - ).await; + let [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] = + create_cctp_router_endpoints_test( + testing_context, + admin_owner_or_assistant.clone(), + custodian_address.clone(), + admin_keypair.clone(), + ) + .await; let local_token_router_endpoint = add_local_router_endpoint_ix( - test_context, + testing_context, admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), - program_id, - &usdc_mint_address, - ).await; - TestRouterEndpoints::new(arbitrum_token_router_endpoint, ethereum_token_router_endpoint, local_token_router_endpoint) -} \ No newline at end of file + ) + .await; + TestRouterEndpoints::new( + arbitrum_token_router_endpoint, + ethereum_token_router_endpoint, + local_token_router_endpoint, + ) +} + +pub async fn get_remote_token_messenger( + test_context: &Rc>, + address: Pubkey, +) -> RemoteTokenMessenger { + let remote_token_messenger_data = test_context + .borrow_mut() + .banks_client + .get_account(address) + .await + .unwrap() + .unwrap() + .data; + let remote_token_messenger = + RemoteTokenMessenger::try_deserialize(&mut remote_token_messenger_data.as_ref()).unwrap(); + remote_token_messenger +} diff --git a/solana/programs/matching-engine/tests/utils/settle_auction.rs b/solana/programs/matching-engine/tests/utils/settle_auction.rs new file mode 100644 index 000000000..f7c9ed577 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/settle_auction.rs @@ -0,0 +1,61 @@ +use anchor_lang::prelude::*; +use anchor_lang::InstructionData; +use anchor_spl::token::spl_token; +use matching_engine::accounts::SettleAuctionComplete as SettleAuctionCompleteCpiAccounts; +use matching_engine::instruction::SettleAuctionComplete; +use solana_program_test::*; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::{Keypair, Signer}; +use solana_sdk::transaction::Transaction; +use std::cell::RefCell; +use std::rc::Rc; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +pub async fn settle_auction_complete( + test_ctx: &Rc>, + payer_signer: &Rc, + usdc_mint_address: &Pubkey, + prepare_order_response_shim_fixture: &super::shims_prepare_order_response::PrepareOrderResponseShimFixture, + auction_state: &Rc>, + matching_engine_program_id: &Pubkey, +) -> Result<()> { + let base_fee_token = usdc_mint_address.clone(); + let event_seeds = EVENT_AUTHORITY_SEED; + let event_authority = + Pubkey::find_program_address(&[event_seeds], matching_engine_program_id).0; + let settle_auction_accounts = SettleAuctionCompleteCpiAccounts { + beneficiary: payer_signer.pubkey(), + base_fee_token: base_fee_token, + prepared_order_response: prepare_order_response_shim_fixture.prepared_order_response, + prepared_custody_token: prepare_order_response_shim_fixture.prepared_custody_token, + auction: auction_state.borrow().auction_address, + best_offer_token: auction_state.borrow().best_offer.best_offer_token, + token_program: spl_token::ID, + event_authority: event_authority, + program: *matching_engine_program_id, + }; + + let settle_auction_complete_cpi = SettleAuctionComplete {}; + + let settle_auction_complete_ix = Instruction { + program_id: *matching_engine_program_id, + accounts: settle_auction_accounts.to_account_metas(Some(false)), + data: settle_auction_complete_cpi.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[settle_auction_complete_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + test_ctx.borrow().last_blockhash, + ); + + test_ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await + .expect("Failed to settle auction"); + + Ok(()) +} diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 5045756dc..e92f5a6c9 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -1,16 +1,51 @@ +use super::account_fixtures::FixtureAccounts; +use super::airdrop::airdrop; +use super::auction::AuctionState; +use super::mint::MintFixture; +use super::program_fixtures::{ + initialise_cctp_message_transmitter, initialise_cctp_token_messenger_minter, + initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, + initialise_verify_shims, initialise_wormhole_core_bridge, +}; +use super::vaa::{create_vaas_test_with_chain_and_address, TestVaaPair, TestVaaPairs, VaaArgs}; +use super::{ + airdrop::airdrop_usdc, + token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, +}; +use super::{Chain, REGISTERED_TOKEN_ROUTERS}; use anchor_lang::AccountDeserialize; +use anchor_spl::token::{ + spl_token::{self, instruction::approve}, + TokenAccount, +}; +use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::{ProgramTest, ProgramTestContext}; use solana_sdk::{ - pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, + pubkey::Pubkey, + signature::{Keypair, Signer}, + transaction::Transaction, }; -use std::rc::Rc; use std::cell::RefCell; -use anchor_spl::token::{spl_token::{self, instruction::approve}, TokenAccount}; -use super::{airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}}; -use super::mint::MintFixture; -use super::program_fixtures::{initialise_upgrade_manager, initialise_cctp_token_messenger_minter, initialise_wormhole_core_bridge, initialise_cctp_message_transmitter, initialise_local_token_router, initialise_post_message_shims, initialise_verify_shims}; -use super::airdrop::airdrop; -use super::account_fixtures::FixtureAccounts; +use std::rc::Rc; + +// Configures the program ID and CCTP mint recipient based on the environment +cfg_if::cfg_if! { + if #[cfg(feature = "mainnet")] { + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"); + //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); + const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); + const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint.json"; + } else if #[cfg(feature = "testnet")] { + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); + //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); + const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; + } else if #[cfg(feature = "localnet")] { + //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); + // const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); + } +} +const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; pub struct PreTestingContext { pub program_test: ProgramTest, @@ -27,19 +62,22 @@ impl PreTestingContext { /// A PreTestingContext struct containing the program data account, testing actors, test context, and fixture accounts pub fn new(program_id: Pubkey, owner_keypair_path: &str) -> Self { let mut program_test = ProgramTest::new( - "matching_engine", // Replace with your program name + "matching_engine", // Replace with your program name program_id, None, ); program_test.set_compute_max_units(1000000000); program_test.set_transaction_account_lock_limit(1000); - // Setup Testing Actors let testing_actors = TestingActors::new(owner_keypair_path); // Initialise Upgrade Manager - let program_data_pubkey = initialise_upgrade_manager(&mut program_test, &program_id, testing_actors.owner.pubkey()); + let program_data_pubkey = initialise_upgrade_manager( + &mut program_test, + &program_id, + testing_actors.owner.pubkey(), + ); // Initialise CCTP Token Messenger Minter initialise_cctp_token_messenger_minter(&mut program_test); @@ -59,7 +97,12 @@ impl PreTestingContext { // Add lookup table accounts FixtureAccounts::add_lookup_table_hack(&mut program_test); - PreTestingContext { program_test, testing_actors, program_data_pubkey, account_fixtures } + PreTestingContext { + program_test, + testing_actors, + program_data_pubkey, + account_fixtures, + } } pub fn add_post_message_shims(&mut self) { @@ -71,28 +114,102 @@ impl PreTestingContext { } } +pub struct TestingState { + pub auction_state: AuctionState, + pub vaas: TestVaaPairs, + pub transfer_direction: TransferDirection, +} +impl Default for TestingState { + fn default() -> Self { + Self { + auction_state: AuctionState::Inactive, + vaas: TestVaaPairs::new(), + transfer_direction: TransferDirection::FromEthereumToArbitrum, + } + } +} pub struct TestingContext { pub program_data_account: Pubkey, // Move this into something smarter pub testing_actors: TestingActors, pub test_context: Rc>, pub fixture_accounts: Option, + pub testing_state: TestingState, } impl TestingContext { - pub async fn new(mut pre_testing_context: PreTestingContext, usdc_mint_fixture_path: &str, usdc_mint_address: Pubkey) -> Self { - let test_context = Rc::new(RefCell::new(pre_testing_context.program_test.start_with_context().await)); - + pub async fn new( + mut pre_testing_context: PreTestingContext, + transfer_direction: TransferDirection, + vaas_test: Option, + ) -> Self { + let test_context = Rc::new(RefCell::new( + pre_testing_context.program_test.start_with_context().await, + )); + // Airdrop to all actors - pre_testing_context.testing_actors.airdrop_all(&test_context).await; + pre_testing_context + .testing_actors + .airdrop_all(&test_context) + .await; // Create USDC mint - let _mint_fixture = MintFixture::new_from_file(&test_context, usdc_mint_fixture_path); + let _mint_fixture = MintFixture::new_from_file(&test_context, USDC_MINT_FIXTURE_PATH); // Create USDC ATAs for all actors that need them - pre_testing_context.testing_actors.create_atas(&test_context, usdc_mint_address).await; + pre_testing_context + .testing_actors + .create_atas(&test_context, USDC_MINT_ADDRESS) + .await; + let testing_state = match vaas_test { + Some(vaas_test) => TestingState { + vaas: vaas_test, + transfer_direction, + ..TestingState::default() + }, + None => TestingState { + transfer_direction, + ..TestingState::default() + }, + }; + TestingContext { + program_data_account: pre_testing_context.program_data_pubkey, + testing_actors: pre_testing_context.testing_actors, + test_context, + fixture_accounts: Some(pre_testing_context.account_fixtures), + testing_state, + } + } + + pub async fn verify_vaas(&self) { + self.testing_state + .vaas + .verify_posted_vaas(&self.test_context) + .await; + } + + pub fn get_vaa_pair(&self, index: usize) -> Option { + if index < self.testing_state.vaas.len() { + Some(self.testing_state.vaas[index].clone()) + } else { + None + } + } - TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, test_context, fixture_accounts: Some(pre_testing_context.account_fixtures) } + pub fn get_fixture_accounts(&self) -> Option { + self.fixture_accounts.clone() + } + + pub fn get_matching_engine_program_id(&self) -> Pubkey { + PROGRAM_ID + } + + pub fn get_usdc_mint_address(&self) -> Pubkey { + USDC_MINT_ADDRESS + } + + pub fn get_cctp_mint_recipient(&self) -> Pubkey { + CCTP_MINT_RECIPIENT } } @@ -103,9 +220,11 @@ pub struct Solver { impl Solver { pub fn new(keypair: Rc, token_account: Option) -> Self { - Self { actor: TestingActor::new(keypair, token_account) } + Self { + actor: TestingActor::new(keypair, token_account), + } } - + pub fn keypair(&self) -> Rc { self.actor.keypair.clone() } @@ -118,18 +237,40 @@ impl Solver { self.actor.token_account.as_ref().map(|t| t.address) } - pub async fn approve_usdc(&self, test_context: &Rc>, delegate: &Pubkey, amount: u64) { + pub async fn approve_usdc( + &self, + test_context: &Rc>, + delegate: &Pubkey, + amount: u64, + ) { // If signer pubkeys are empty, it means that the owner is the signer - let last_blockhash = test_context.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - let approve_ix = approve(&spl_token::ID, &self.token_account_address().unwrap(), delegate, &self.actor.pubkey(), &[], amount).expect("Failed to create approve USDC instruction"); + let last_blockhash = test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let approve_ix = approve( + &spl_token::ID, + &self.token_account_address().unwrap(), + delegate, + &self.actor.pubkey(), + &[], + amount, + ) + .expect("Failed to create approve USDC instruction"); let transaction = Transaction::new_signed_with_payer( &[approve_ix], Some(&self.actor.pubkey()), &[&self.actor.keypair()], last_blockhash, ); - - test_context.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to approve USDC"); + + test_context + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to approve USDC"); } pub async fn get_balance(&self, test_context: &Rc>) -> u64 { @@ -145,13 +286,21 @@ pub struct TestingActor { impl std::fmt::Debug for TestingActor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", self.keypair.pubkey(), self.token_account) + write!( + f, + "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", + self.keypair.pubkey(), + self.token_account + ) } } impl TestingActor { pub fn new(keypair: Rc, token_account: Option) -> Self { - Self { keypair, token_account } + Self { + keypair, + token_account, + } } pub fn pubkey(&self) -> Pubkey { self.keypair.pubkey() @@ -166,11 +315,16 @@ impl TestingActor { pub async fn get_balance(&self, test_context: &Rc>) -> u64 { if let Some(token_account) = self.token_account_address() { - let account = test_context.borrow_mut().banks_client.get_account(token_account).await.unwrap().unwrap(); + let account = test_context + .borrow_mut() + .banks_client + .get_account(token_account) + .await + .unwrap() + .unwrap(); let token_account = TokenAccount::try_deserialize(&mut &account.data[..]).unwrap(); token_account.amount - } - else { + } else { 0 } } @@ -201,7 +355,14 @@ impl TestingActors { Solver::new(Rc::new(Keypair::new()), None), ]); let liquidator = TestingActor::new(Rc::new(Keypair::new()), None); - Self { owner, owner_assistant, fee_recipient, relayer, solvers, liquidator } + Self { + owner, + owner_assistant, + fee_recipient, + relayer, + solvers, + liquidator, + } } pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { @@ -225,11 +386,17 @@ impl TestingActors { } airdrop(test_context, &self.liquidator.pubkey(), 10000000000).await; } - + /// Set up ATAs for Various Owners - async fn create_atas(&mut self, test_context: &Rc>, usdc_mint_address: Pubkey) { + async fn create_atas( + &mut self, + test_context: &Rc>, + usdc_mint_address: Pubkey, + ) { for actor in self.token_account_actors() { - let usdc_ata = create_token_account(test_context.clone(), &actor.keypair(), &usdc_mint_address).await; + let usdc_ata = + create_token_account(test_context.clone(), &actor.keypair(), &usdc_mint_address) + .await; airdrop_usdc(test_context, &usdc_ata.address, 420_000__000_000).await; actor.token_account = Some(usdc_ata); } @@ -237,12 +404,19 @@ impl TestingActors { /// Add solvers to the testing actors #[allow(dead_code)] - async fn add_solvers(&mut self, test_context: &Rc>, num_solvers: usize, usdc_mint_address: Pubkey) { + async fn add_solvers( + &mut self, + test_context: &Rc>, + num_solvers: usize, + usdc_mint_address: Pubkey, + ) { for _ in 0..num_solvers { let keypair = Rc::new(Keypair::new()); - let usdc_ata = create_token_account(test_context.clone(), &keypair, &usdc_mint_address).await; + let usdc_ata = + create_token_account(test_context.clone(), &keypair, &usdc_mint_address).await; airdrop(test_context, &keypair.pubkey(), 10000000000).await; - self.solvers.push(Solver::new(keypair.clone(), Some(usdc_ata))); + self.solvers + .push(Solver::new(keypair.clone(), Some(usdc_ata))); } } } @@ -265,7 +439,7 @@ pub async fn fast_forward_slots(test_context: &Rc>, .expect("Failed to warp to slot"); current_slot += 1; } - + // Optionally, process a transaction to ensure the new slot is recognized let recent_blockhash = test_context.borrow().last_blockhash; let payer = test_context.borrow().payer.pubkey(); @@ -275,11 +449,83 @@ pub async fn fast_forward_slots(test_context: &Rc>, &[&test_context.borrow().payer], recent_blockhash, ); - + test_context .borrow_mut() .banks_client .process_transaction(tx) .await .expect("Failed to process transaction after warping"); -} \ No newline at end of file +} + +pub enum ShimMode { + None, + PostVaa, + // VerifySignature, + VerifyAndPostSignature, +} + +#[derive(Copy, Clone)] +pub enum TransferDirection { + FromArbitrumToEthereum, + FromEthereumToArbitrum, +} + +pub async fn setup_environment( + shim_mode: ShimMode, + transfer_direction: TransferDirection, + vaa_args: Option, +) -> TestingContext { + let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); + let vaas_test: Option = match vaa_args { + Some(vaa_args) => { + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum] + .clone() + .try_into() + .expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum] + .clone() + .try_into() + .expect("Failed to convert registered token router address to bytes [u8; 32]"); + match transfer_direction { + TransferDirection::FromArbitrumToEthereum => { + Some(create_vaas_test_with_chain_and_address( + &mut pre_testing_context.program_test, + USDC_MINT_ADDRESS, + CCTP_MINT_RECIPIENT, + Chain::Arbitrum, + Chain::Ethereum, + arbitrum_emitter_address, + ethereum_emitter_address, + vaa_args, + )) + } + TransferDirection::FromEthereumToArbitrum => { + Some(create_vaas_test_with_chain_and_address( + &mut pre_testing_context.program_test, + USDC_MINT_ADDRESS, + CCTP_MINT_RECIPIENT, + Chain::Ethereum, + Chain::Arbitrum, + ethereum_emitter_address, + arbitrum_emitter_address, + vaa_args, + )) + } + } + } + None => None, + }; + match shim_mode { + ShimMode::None => {} + ShimMode::PostVaa => {} + // ShimMode::VerifySignature => { + // pre_testing_context.add_verify_shims(); + // } + ShimMode::VerifyAndPostSignature => { + pre_testing_context.add_verify_shims(); + pre_testing_context.add_post_message_shims(); + } + }; + TestingContext::new(pre_testing_context, transfer_direction, vaas_test).await +} diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/utils/shims.rs index e6ad05cec..9fccadea4 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/utils/shims.rs @@ -1,8 +1,24 @@ +use super::setup::TestingContext; +use super::{constants::*, setup::Solver}; use anchor_lang::prelude::*; +use base64::Engine; use common::messages::FastMarketOrder; -use wormhole_io::TypePrefixedPayload; -use super::{constants::*, setup::Solver}; -use wormhole_svm_shim::{post_message, verify_vaa}; +use matching_engine::fallback::close_fast_market_order::{ + CloseFastMarketOrder as CloseFastMarketOrderFallback, + CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, +}; +use matching_engine::fallback::initialise_fast_market_order::{ + InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, + InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, + InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, +}; +use matching_engine::fallback::place_initial_offer::{ + PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, + PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, + PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, +}; +use matching_engine::state::{Auction, FastMarketOrder as FastMarketOrderState}; +use solana_program_test::ProgramTestContext; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, hash::Hash, @@ -12,31 +28,14 @@ use solana_sdk::{ signer::Signer, transaction::{Transaction, VersionedTransaction}, }; -use solana_program_test::ProgramTestContext; -use std::{rc::Rc, str::FromStr}; use std::cell::RefCell; +use std::{rc::Rc, str::FromStr}; +use wormhole_io::TypePrefixedPayload; +use wormhole_svm_definitions::borsh::GuardianSignatures; use wormhole_svm_definitions::{ - solana::Finality, - find_emitter_sequence_address, - find_shim_message_address, + find_emitter_sequence_address, find_shim_message_address, solana::Finality, }; -use base64::Engine; -use matching_engine::state::{Auction, FastMarketOrder as FastMarketOrderState}; -use matching_engine::fallback::place_initial_offer::{ - PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, - PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, - PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, -}; -use matching_engine::fallback::initialise_fast_market_order::{ - InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, - InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, - InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, -}; -use matching_engine::fallback::close_fast_market_order::{ - CloseFastMarketOrder as CloseFastMarketOrderFallback, - CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, -}; -use wormhole_svm_definitions::borsh::GuardianSignatures; +use wormhole_svm_shim::{post_message, verify_vaa}; #[allow(dead_code)] struct BumpCosts { @@ -51,7 +50,16 @@ fn bump_cu_cost(bump: u8) -> u64 { #[allow(dead_code)] const EMITTER_SEQUENCE_SEED: &[u8] = b"Sequence"; -pub async fn set_up_post_message_transaction_test(test_ctx: &Rc>, payer_signer: &Rc, emitter_signer: &Rc, recent_blockhash: Hash) { +pub async fn set_up_post_message_transaction_test( + test_ctx: &Rc>, + payer_signer: &Rc, + emitter_signer: &Rc, +) { + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Could not get last blockhash"); let (transaction, _bump_costs) = set_up_post_message_transaction( b"All your base are belong to us", &payer_signer.clone().to_owned(), @@ -59,7 +67,9 @@ pub async fn set_up_post_message_transaction_test(test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32) -> Result { - let new_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); - let transaction = post_signatures_transaction(payer_signer, signatures_signer, guardian_set_index, guardian_signatures.len() as u8, &guardian_signatures, new_blockhash); - test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to add guardian signatures account"); - +pub async fn add_guardian_signatures_account( + test_ctx: &Rc>, + payer_signer: &Rc, + signatures_signer: &Rc, + guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, + guardian_set_index: u32, +) -> Result { + let new_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let transaction = post_signatures_transaction( + payer_signer, + signatures_signer, + guardian_set_index, + guardian_signatures.len() as u8, + &guardian_signatures, + new_blockhash, + ); + test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to add guardian signatures account"); + Ok(signatures_signer.pubkey()) } #[allow(dead_code)] /// Post signatures before the auction is created. -pub async fn set_up_verify_shims_test(test_ctx: &Rc>, payer_signer: &Rc) -> Result { +pub async fn set_up_verify_shims_test( + test_ctx: &Rc>, + payer_signer: &Rc, +) -> Result { let guardian_signatures_signer = Rc::new(Keypair::new()); - let (transaction, decoded_vaa)= set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); + let (transaction, decoded_vaa) = + set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); let _details = { - let out = test_ctx.borrow_mut().banks_client + let out = test_ctx + .borrow_mut() + .banks_client .simulate_transaction(transaction.clone()) .await .unwrap(); @@ -207,7 +235,9 @@ pub async fn set_up_verify_shims_test(test_ctx: &Rc> }; { - let out = test_ctx.borrow_mut().banks_client + let out = test_ctx + .borrow_mut() + .banks_client .process_transaction(transaction) .await; assert!(out.is_ok()); @@ -215,7 +245,9 @@ pub async fn set_up_verify_shims_test(test_ctx: &Rc> }; // Check guardian signatures account after processing the transaction. - let guardian_signatures_info = test_ctx.borrow_mut().banks_client + let guardian_signatures_info = test_ctx + .borrow_mut() + .banks_client .get_account(guardian_signatures_signer.pubkey()) .await .unwrap() @@ -232,7 +264,10 @@ pub async fn set_up_verify_shims_test(test_ctx: &Rc> assert_eq!(account_data.len(), expected_length); assert_eq!( - wormhole_svm_definitions::borsh::deserialize_with_discriminator::(&account_data[..]).unwrap(), + wormhole_svm_definitions::borsh::deserialize_with_discriminator::< + wormhole_svm_definitions::borsh::GuardianSignatures, + >(&account_data[..]) + .unwrap(), expected_guardian_signatures_data ); Ok(guardian_signatures_signer.pubkey()) @@ -254,7 +289,10 @@ impl From<&str> for DecodedVaa { let total_signatures = buf[5]; let body = buf - .drain((6 + total_signatures as usize * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH)..) + .drain( + (6 + total_signatures as usize + * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH).., + ) .collect(); let mut guardian_signatures = Vec::with_capacity(total_signatures as usize); @@ -262,7 +300,9 @@ impl From<&str> for DecodedVaa { for i in 0..usize::from(total_signatures) { let offset = 6 + i * 66; let mut signature = [0; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; - signature.copy_from_slice(&buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]); + signature.copy_from_slice( + &buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH], + ); guardian_signatures.push(signature); } @@ -276,17 +316,39 @@ impl From<&str> for DecodedVaa { } #[allow(dead_code)] -fn set_up_verify_shims_transaction(test_ctx: &Rc>, payer_signer: &Rc, guardian_signatures_signer: &Rc) -> (VersionedTransaction, DecodedVaa) { +fn set_up_verify_shims_transaction( + test_ctx: &Rc>, + payer_signer: &Rc, + guardian_signatures_signer: &Rc, +) -> (VersionedTransaction, DecodedVaa) { const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; let decoded_vaa = DecodedVaa::from(VAA); let decoded_vaa_clone = decoded_vaa.clone(); assert_eq!(decoded_vaa.total_signatures, 13); let recent_blockhash = test_ctx.borrow().last_blockhash; - let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = &decoded_vaa.guardian_signatures; - (post_signatures_transaction(payer_signer, guardian_signatures_signer, decoded_vaa.guardian_set_index, decoded_vaa.total_signatures, guardian_signatures_vec, recent_blockhash), decoded_vaa_clone) + let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = + &decoded_vaa.guardian_signatures; + ( + post_signatures_transaction( + payer_signer, + guardian_signatures_signer, + decoded_vaa.guardian_set_index, + decoded_vaa.total_signatures, + guardian_signatures_vec, + recent_blockhash, + ), + decoded_vaa_clone, + ) } -fn post_signatures_transaction(payer_signer: &Rc, guardian_signatures_signer: &Rc, guardian_set_index: u32, total_signatures: u8, guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, recent_blockhash: Hash) -> VersionedTransaction { +fn post_signatures_transaction( + payer_signer: &Rc, + guardian_signatures_signer: &Rc, + guardian_set_index: u32, + total_signatures: u8, + guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, + recent_blockhash: Hash, +) -> VersionedTransaction { let post_signatures_ix = verify_vaa::PostSignatures { program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, accounts: verify_vaa::PostSignaturesAccounts { @@ -349,38 +411,91 @@ fn generate_expected_guardian_signatures_info( } pub struct PlaceInitialOfferShimFixture { - pub auction_address: Pubkey, - pub auction_custody_token_address: Pubkey, + pub auction_state: Rc>, pub guardian_set_pubkey: Pubkey, pub guardian_signatures_pubkey: Pubkey, pub fast_market_order_address: Pubkey, pub fast_market_order: FastMarketOrderState, } -/// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. -pub async fn place_initial_offer_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, vaa_data: &super::vaa::PostedVaaData, solver: Solver, auction_accounts: &super::auction::AuctionAccounts, offer_price: u64) -> Result { - let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); +/// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. +pub async fn place_initial_offer_fallback( + testing_context: &mut TestingContext, + payer_signer: &Rc, + program_id: &Pubkey, + wormhole_program_id: &Pubkey, + vaa_data: &super::vaa::PostedVaaData, + solver: Solver, + auction_accounts: &super::auction::AuctionAccounts, + offer_price: u64, +) -> Result { + let test_ctx = &testing_context.test_context; + let (fast_market_order, vaa_data) = + create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); + + let auction_address = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, &fast_market_order.digest()], + &program_id, + ) + .0; + let auction_custody_token_address = Pubkey::find_program_address( + &[ + matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_address.as_ref(), + ], + &program_id, + ) + .0; - let auction_address = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &fast_market_order.digest], &program_id).0; - let auction_custody_token_address = Pubkey::find_program_address(&[matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_address.as_ref()], &program_id).0; - // Approve the transfer authority - let transfer_authority = Pubkey::find_program_address(&[common::TRANSFER_AUTHORITY_SEED_PREFIX, &auction_address.to_bytes(), &offer_price.to_be_bytes()], &program_id).0; - - solver.approve_usdc(test_ctx, &transfer_authority, 420_000__000_000).await; - + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + + solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; + let solver_usdc_balance = solver.get_balance(test_ctx).await; println!("Solver USDC balance: {:?}", solver_usdc_balance); // Create the guardian set and signatures - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = create_guardian_signatures(test_ctx, payer_signer, &vaa_data, wormhole_program_id, None).await; - + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = + create_guardian_signatures(test_ctx, payer_signer, &vaa_data, wormhole_program_id, None) + .await; + // Create the fast market order account - let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], program_id).0; + let fast_market_order_account = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.refund_recipient, + ], + program_id, + ) + .0; - let create_fast_market_order_ix = initialise_fast_market_order_fallback_instruction(payer_signer, program_id, fast_market_order, guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump); + let create_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + payer_signer, + program_id, + fast_market_order, + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ); - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price, vaa_data.sequence, vaa_data.vaa_time, vaa_data.consistency_level); + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( + offer_price, + vaa_data.sequence, + vaa_data.vaa_time, + vaa_data.consistency_level, + ); let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { signer: &payer_signer.pubkey(), @@ -401,18 +516,43 @@ pub async fn place_initial_offer_fallback(test_ctx: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, guardian_set_pubkey: Pubkey, guardian_signatures_pubkey: Pubkey, guardian_set_bump: u8) -> solana_program::instruction::Instruction { - let fast_market_order_account = Pubkey::find_program_address(&[FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest, &fast_market_order.refund_recipient], program_id).0; - +pub fn initialise_fast_market_order_fallback_instruction( + payer_signer: &Rc, + program_id: &Pubkey, + fast_market_order: FastMarketOrderState, + guardian_set_pubkey: Pubkey, + guardian_signatures_pubkey: Pubkey, + guardian_set_bump: u8, +) -> solana_program::instruction::Instruction { + let fast_market_order_account = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.refund_recipient, + ], + program_id, + ) + .0; + let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { signer: &payer_signer.pubkey(), fast_market_order_account: &fast_market_order_account, @@ -436,24 +591,48 @@ pub fn initialise_fast_market_order_fallback_instruction(payer_signer: &Rc>, refund_recipient_keypair: &Rc, program_id: &Pubkey, fast_market_order_address: &Pubkey) { - let recent_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new blockhash"); +pub async fn close_fast_market_order_fallback( + test_ctx: &Rc>, + refund_recipient_keypair: &Rc, + program_id: &Pubkey, + fast_market_order_address: &Pubkey, +) { + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); let close_fast_market_order_ix = CloseFastMarketOrderFallback { program_id: program_id, accounts: CloseFastMarketOrderFallbackAccounts { fast_market_order: fast_market_order_address, refund_recipient: &refund_recipient_keypair.pubkey(), }, - }.instruction(); + } + .instruction(); - let transaction = Transaction::new_signed_with_payer(&[close_fast_market_order_ix], Some(&refund_recipient_keypair.pubkey()), &[refund_recipient_keypair], recent_blockhash); - test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to close fast market order"); + let transaction = Transaction::new_signed_with_payer( + &[close_fast_market_order_ix], + Some(&refund_recipient_keypair.pubkey()), + &[refund_recipient_keypair], + recent_blockhash, + ); + test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to close fast market order"); } -pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::PostedVaaData, refund_recipient: Pubkey) -> (FastMarketOrderState, super::vaa::PostedVaaData) { +pub fn create_fast_market_order_state_from_vaa_data( + vaa_data: &super::vaa::PostedVaaData, + refund_recipient: Pubkey, +) -> (FastMarketOrderState, super::vaa::PostedVaaData) { let vaa_data = super::vaa::PostedVaaData { consistency_level: vaa_data.consistency_level, vaa_time: vaa_data.vaa_time, @@ -461,7 +640,7 @@ pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::Poste emitter_chain: vaa_data.emitter_chain, emitter_address: vaa_data.emitter_address, payload: vaa_data.payload.clone(), - nonce: 0, + nonce: vaa_data.nonce, vaa_signature_account: vaa_data.vaa_signature_account, submission_time: 0, }; @@ -474,18 +653,18 @@ pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::Poste ); let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); - + let redeemer_message_fixed_length = { let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) - + if !order.redeemer_message.is_empty() { // Calculate how many bytes to copy (min of message length and array size) let copy_len = std::cmp::min(order.redeemer_message.len(), 512); - + // Copy the bytes from the message to the fixed array fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); } - + fixed_array }; let fast_market_order = FastMarketOrderState::new( @@ -500,26 +679,53 @@ pub fn create_fast_market_order_state_from_vaa_data(vaa_data: &super::vaa::Poste order.max_fee, order.init_auction_fee, redeemer_message_fixed_length, - vaa_data.digest(), refund_recipient.to_bytes(), vaa_data.sequence, vaa_data.vaa_time, + vaa_data.nonce, vaa_data.emitter_chain, + vaa_data.consistency_level, vaa_data.emitter_address, ); assert_eq!(fast_market_order.redeemer, order.redeemer); - assert_eq!(vaa_message.digest(&fast_market_order).as_ref(), vaa_data.digest().as_ref()); + assert_eq!( + vaa_message.digest(&fast_market_order).as_ref(), + vaa_data.digest().as_ref() + ); (fast_market_order, vaa_data) } -pub async fn create_guardian_signatures(test_ctx: &Rc>, payer_signer: &Rc, vaa_data: &super::vaa::PostedVaaData, wormhole_program_id: &Pubkey, guardian_signature_signer: Option<&Rc>) -> (Pubkey, Pubkey, u8) { +pub async fn create_guardian_signatures( + test_ctx: &Rc>, + payer_signer: &Rc, + vaa_data: &super::vaa::PostedVaaData, + wormhole_program_id: &Pubkey, + guardian_signature_signer: Option<&Rc>, +) -> (Pubkey, Pubkey, u8) { let new_keypair = Rc::new(Keypair::new()); let guardian_signature_signer = guardian_signature_signer.unwrap_or(&new_keypair); - let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address(0_u32.to_be_bytes(), &wormhole_program_id); - let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY).expect("Failed to parse guardian secret key"); + let (guardian_set_pubkey, guardian_set_bump) = + wormhole_svm_definitions::find_guardian_set_address( + 0_u32.to_be_bytes(), + &wormhole_program_id, + ); + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY) + .expect("Failed to parse guardian secret key"); let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); - let guardian_signatures_pubkey = add_guardian_signatures_account(test_ctx, payer_signer, guardian_signature_signer, vec![guardian_set_signatures], 0).await.expect("Failed to post guardian signatures"); - (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) + let guardian_signatures_pubkey = add_guardian_signatures_account( + test_ctx, + payer_signer, + guardian_signature_signer, + vec![guardian_set_signatures], + 0, + ) + .await + .expect("Failed to post guardian signatures"); + ( + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ) } diff --git a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs index 027fe1802..f64c05b89 100644 --- a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/utils/shims_execute_order.rs @@ -1,19 +1,25 @@ +use super::setup::TransferDirection; +use super::{constants::*, setup::Solver}; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use common::wormhole_cctp_solana::cctp::{MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID}; -use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; -use super::{constants::*, setup::Solver}; -use solana_sdk::{ - pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction +use common::wormhole_cctp_solana::cctp::{ + MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; +use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; use solana_program_test::ProgramTestContext; -use std::rc::Rc; +use solana_sdk::{ + pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction, +}; use std::cell::RefCell; +use std::rc::Rc; +use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; use wormhole_svm_definitions::{ - solana::{POST_MESSAGE_SHIM_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR}, EVENT_AUTHORITY_SEED + solana::{ + CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, POST_MESSAGE_SHIM_EVENT_AUTHORITY, + POST_MESSAGE_SHIM_PROGRAM_ID, + }, + EVENT_AUTHORITY_SEED, }; -use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; - pub struct ExecuteOrderFallbackAccounts { pub custodian: Pubkey, @@ -30,18 +36,38 @@ pub struct ExecuteOrderFallbackAccounts { } impl ExecuteOrderFallbackAccounts { - pub fn new(auction_accounts: &super::auction::AuctionAccounts, place_initial_offer_fixture: &super::shims::PlaceInitialOfferShimFixture, signer: &Pubkey, fixture_accounts: &super::account_fixtures::FixtureAccounts) -> Self { + pub fn new( + auction_accounts: &super::auction::AuctionAccounts, + place_initial_offer_fixture: &super::shims::PlaceInitialOfferShimFixture, + signer: &Pubkey, + fixture_accounts: &super::account_fixtures::FixtureAccounts, + transfer_direction: TransferDirection, + ) -> Self { + let remote_token_messenger = match transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + fixture_accounts.arbitrum_remote_token_messenger + } + TransferDirection::FromArbitrumToEthereum => { + fixture_accounts.ethereum_remote_token_messenger + } + }; Self { custodian: auction_accounts.custodian, fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, - active_auction: place_initial_offer_fixture.auction_address, - active_auction_custody_token: place_initial_offer_fixture.auction_custody_token_address, + active_auction: place_initial_offer_fixture + .auction_state + .borrow() + .auction_address, + active_auction_custody_token: place_initial_offer_fixture + .auction_state + .borrow() + .auction_custody_token_address, active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, initial_participant: signer.clone(), to_router_endpoint: auction_accounts.to_router_endpoint, - remote_token_messenger: fixture_accounts.ethereum_remote_token_messenger, + remote_token_messenger, token_messenger: fixture_accounts.token_messenger, } } @@ -62,68 +88,111 @@ pub struct ExecuteOrderFallbackFixtureAccounts { pub token_messenger_minter_event_authority: Pubkey, } -pub async fn execute_order_fallback(test_ctx: &Rc>, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts) -> Result { - +pub async fn execute_order_fallback( + test_ctx: &Rc>, + payer_signer: &Rc, + program_id: &Pubkey, + solver: Solver, + execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, +) -> Result { // Get target chain and use as remote address - let cctp_message = Pubkey::find_program_address(&[common::CCTP_MESSAGE_SEED_PREFIX, &execute_order_fallback_accounts.active_auction.to_bytes()], program_id).0; - let token_messenger_minter_sender_authority = Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let messenger_transmitter_config = Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let token_messenger = Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let cctp_message = Pubkey::find_program_address( + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + &execute_order_fallback_accounts.active_auction.to_bytes(), + ], + program_id, + ) + .0; + let token_messenger_minter_sender_authority = + Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let messenger_transmitter_config = + Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let token_messenger = + Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let remote_token_messenger = execute_order_fallback_accounts.remote_token_messenger; - let token_minter = Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let local_token = Pubkey::find_program_address(&[b"local_token", &USDC_MINT.to_bytes()], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let token_messenger_minter_event_authority = &Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address(&execute_order_fallback_accounts.custodian, &CORE_BRIDGE_PROGRAM_ID).0; - let post_message_message = wormhole_svm_definitions::find_shim_message_address(&execute_order_fallback_accounts.custodian, &POST_MESSAGE_SHIM_PROGRAM_ID).0; + let token_minter = + Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let local_token = Pubkey::find_program_address( + &[b"local_token", &USDC_MINT.to_bytes()], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let token_messenger_minter_event_authority = + &Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID) + .0; + let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address( + &execute_order_fallback_accounts.custodian, + &CORE_BRIDGE_PROGRAM_ID, + ) + .0; + let post_message_message = wormhole_svm_definitions::find_shim_message_address( + &execute_order_fallback_accounts.custodian, + &POST_MESSAGE_SHIM_PROGRAM_ID, + ) + .0; let executor_token = solver.actor.token_account_address().unwrap(); let execute_order_ix_accounts = ExecuteOrderShimAccounts { - signer: &payer_signer.pubkey(), // 0 - cctp_message: &cctp_message, // 1 + signer: &payer_signer.pubkey(), // 0 + cctp_message: &cctp_message, // 1 custodian: &execute_order_fallback_accounts.custodian, // 2 fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, // 3 active_auction: &execute_order_fallback_accounts.active_auction, // 4 active_auction_custody_token: &execute_order_fallback_accounts.active_auction_custody_token, // 5 active_auction_config: &execute_order_fallback_accounts.active_auction_config, // 6 - active_auction_best_offer_token: &execute_order_fallback_accounts.active_auction_best_offer_token, // 7 - executor_token: &executor_token, // 8 - initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, // 9 - initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 - to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 - post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 - post_message_sequence: &post_message_sequence, // 13 - post_message_message: &post_message_message, // 14 - cctp_deposit_for_burn_mint: &USDC_MINT, // 15 - cctp_deposit_for_burn_token_messenger_minter_sender_authority: &token_messenger_minter_sender_authority, // 16 + active_auction_best_offer_token: &execute_order_fallback_accounts + .active_auction_best_offer_token, // 7 + executor_token: &executor_token, // 8 + initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, // 9 + initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 + to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 + post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 + post_message_sequence: &post_message_sequence, // 13 + post_message_message: &post_message_message, // 14 + cctp_deposit_for_burn_mint: &USDC_MINT, // 15 + cctp_deposit_for_burn_token_messenger_minter_sender_authority: + &token_messenger_minter_sender_authority, // 16 cctp_deposit_for_burn_message_transmitter_config: &messenger_transmitter_config, // 17 - cctp_deposit_for_burn_token_messenger: &token_messenger, // 18 - cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, // 19 - cctp_deposit_for_burn_token_minter: &token_minter, // 20 - cctp_deposit_for_burn_local_token: &local_token, // 21 - cctp_deposit_for_burn_token_messenger_minter_event_authority: &token_messenger_minter_event_authority, // 22 - cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 - cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 - core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 - core_bridge_config: &CORE_BRIDGE_CONFIG, // 26 - core_bridge_fee_collector: &CORE_BRIDGE_FEE_COLLECTOR, // 27 - post_message_shim_event_authority: &POST_MESSAGE_SHIM_EVENT_AUTHORITY, // 28 - system_program: &solana_program::system_program::ID, // 29 - token_program: &spl_token::ID, // 30 - clock: &solana_program::clock::Clock::id(), // 31 + cctp_deposit_for_burn_token_messenger: &token_messenger, // 18 + cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, // 19 + cctp_deposit_for_burn_token_minter: &token_minter, // 20 + cctp_deposit_for_burn_local_token: &local_token, // 21 + cctp_deposit_for_burn_token_messenger_minter_event_authority: + &token_messenger_minter_event_authority, // 22 + cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 + cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 + core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 + core_bridge_config: &CORE_BRIDGE_CONFIG, // 26 + core_bridge_fee_collector: &CORE_BRIDGE_FEE_COLLECTOR, // 27 + post_message_shim_event_authority: &POST_MESSAGE_SHIM_EVENT_AUTHORITY, // 28 + system_program: &solana_program::system_program::ID, // 29 + token_program: &spl_token::ID, // 30 + clock: &solana_program::clock::Clock::id(), // 31 }; - let execute_order_ix = ExecuteOrderCctpShim { program_id: program_id, accounts: execute_order_ix_accounts, - }.instruction(); + } + .instruction(); // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; super::setup::fast_forward_slots(test_ctx, 1).await; println!("Fast forwarded 1 slots"); - let transaction = Transaction::new_signed_with_payer(&[execute_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); - test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to execute order"); + let transaction = Transaction::new_signed_with_payer( + &[execute_order_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + recent_blockhash, + ); + test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to execute order"); Ok(ExecuteOrderFallbackFixture { cctp_message, @@ -134,7 +203,7 @@ pub async fn execute_order_fallback(test_ctx: &Rc>, token_messenger, remote_token_messenger, token_messenger_minter_sender_authority, - token_messenger_minter_event_authority : *token_messenger_minter_event_authority, + token_messenger_minter_event_authority: *token_messenger_minter_event_authority, }, }) -} \ No newline at end of file +} diff --git a/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs index b4a5fd2d9..928bd1c18 100644 --- a/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs @@ -1,19 +1,22 @@ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use common::messages::SlowOrderResponse; +use common::messages::raw::LiquidityLayerDepositMessage; +use common::wormhole_cctp_solana::cctp::{ + MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, +}; use common::wormhole_cctp_solana::messages::Deposit; use common::wormhole_cctp_solana::utils::CctpMessage; use matching_engine::fallback::prepare_order_response::{ - DepositMessage, FinalizedVaaMessage, PrepareOrderResponseCctpShim as PrepareOrderResponseCctpShimIx, PrepareOrderResponseCctpShimAccounts, PrepareOrderResponseCctpShimData}; + FinalizedVaaMessage, PrepareOrderResponseCctpShim as PrepareOrderResponseCctpShimIx, + PrepareOrderResponseCctpShimAccounts, PrepareOrderResponseCctpShimData, +}; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; use solana_program_test::ProgramTestContext; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; -use wormhole_io::TypePrefixedPayload; -use std::rc::Rc; use std::cell::RefCell; -use common::wormhole_cctp_solana::cctp::{MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID}; +use std::rc::Rc; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; use super::account_fixtures::FixtureAccounts; @@ -52,33 +55,53 @@ struct UsedNonces; impl UsedNonces { pub const MAX_NONCES: u64 = 6400; pub fn address(remote_domain: u32, nonce: u64) -> (Pubkey, u8) { - let first_nonce = (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket + let first_nonce = if nonce == 0 { + 0 + } else { + (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1 + }; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket let remote_domain_converted = remote_domain.to_string(); let first_nonce_converted = first_nonce.to_string(); - Pubkey::find_program_address(&[ - b"used_nonces", - remote_domain_converted.as_bytes(), - first_nonce_converted.as_bytes(), - ], &MESSAGE_TRANSMITTER_PROGRAM_ID) + Pubkey::find_program_address( + &[ + b"used_nonces", + remote_domain_converted.as_bytes(), + first_nonce_converted.as_bytes(), + ], + &MESSAGE_TRANSMITTER_PROGRAM_ID, + ) } } impl PrepareOrderResponseShimAccountsFixture { - pub fn new(signer: &Pubkey, - fixture_accounts: &FixtureAccounts, - execute_order_fixture: &ExecuteOrderFallbackFixture, - initial_offer_fixture: &PlaceInitialOfferShimFixture, + pub fn new( + signer: &Pubkey, + fixture_accounts: &FixtureAccounts, + execute_order_fixture: &ExecuteOrderFallbackFixture, + initial_offer_fixture: &PlaceInitialOfferShimFixture, initialize_fixture: &InitializeFixture, - to_router_endpoint: &Pubkey, from_router_endpoint: &Pubkey, + to_router_endpoint: &Pubkey, usdc_mint_address: &Pubkey, cctp_message_decoded: &CctpMessageDecoded, guardian_set: &Pubkey, guardian_set_signatures: &Pubkey, ) -> Self { - let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let cctp_message_transmitter_authority = Pubkey::find_program_address(&[b"message_transmitter_authority", &TOKEN_MESSENGER_MINTER_PROGRAM_ID.as_ref()], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let (cctp_used_nonces_pda, _cctp_used_nonces_bump) = UsedNonces::address(cctp_message_decoded.source_domain, cctp_message_decoded.nonce); + let cctp_message_transmitter_event_authority = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID) + .0; + let cctp_message_transmitter_authority = Pubkey::find_program_address( + &[ + b"message_transmitter_authority", + &TOKEN_MESSENGER_MINTER_PROGRAM_ID.as_ref(), + ], + &MESSAGE_TRANSMITTER_PROGRAM_ID, + ) + .0; + let (cctp_used_nonces_pda, _cctp_used_nonces_bump) = UsedNonces::address( + cctp_message_decoded.source_domain, + cctp_message_decoded.nonce, + ); Self { signer: signer.clone(), custodian: initialize_fixture.get_custodian_address(), @@ -91,16 +114,20 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_message_transmitter_authority: cctp_message_transmitter_authority.clone(), cctp_message_transmitter_config: fixture_accounts.message_transmitter_config.clone(), cctp_used_nonces: cctp_used_nonces_pda.clone(), - cctp_message_transmitter_event_authority: cctp_message_transmitter_event_authority.clone(), - cctp_token_messenger: fixture_accounts.arbitrum_remote_token_messenger.clone(), + cctp_message_transmitter_event_authority: cctp_message_transmitter_event_authority + .clone(), + cctp_token_messenger: fixture_accounts.token_messenger.clone(), cctp_remote_token_messenger: fixture_accounts.ethereum_remote_token_messenger.clone(), cctp_token_minter: fixture_accounts.token_minter.clone(), cctp_local_token: fixture_accounts.usdc_local_token.clone(), cctp_token_pair: fixture_accounts.usdc_token_pair.clone(), - cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), + cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), cctp_token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, cctp_message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, - cctp_token_messenger_minter_event_authority: execute_order_fixture.accounts.token_messenger_minter_event_authority.clone(), + cctp_token_messenger_minter_event_authority: execute_order_fixture + .accounts + .token_messenger_minter_event_authority + .clone(), guardian_set: guardian_set.clone(), guardian_set_signatures: guardian_set_signatures.clone(), } @@ -120,7 +147,8 @@ pub struct PrepareOrderResponseShimDataFixture { pub finalized_vaa_message_emitter_chain: u16, pub finalized_vaa_message_emitter_address: [u8; 32], pub finalized_vaa_message_base_fee: u64, - pub deposit_message: DepositMessage, + pub vaa_payload: Vec, + pub deposit_payload: Vec, pub fast_market_order: FastMarketOrderState, pub guardian_set_bump: u8, } @@ -135,18 +163,6 @@ impl PrepareOrderResponseShimDataFixture { fast_market_order: &FastMarketOrderState, guardian_set_bump: u8, ) -> Self { - let deposit_message = DepositMessage { - token_address: deposit.token_address, - amount: deposit.amount.to_le_bytes(), - source_cctp_domain: deposit.source_cctp_domain, - destination_cctp_domain: deposit.destination_cctp_domain, - cctp_nonce: deposit.cctp_nonce, - burn_source: deposit.burn_source, - mint_recipient: deposit.mint_recipient, - digest: deposit_vaa_data.digest(), - payload_len: deposit_vaa_data.payload.len() as u16, - payload: deposit_vaa_data.payload.clone(), - }; Self { encoded_cctp_message, cctp_attestation, @@ -155,14 +171,13 @@ impl PrepareOrderResponseShimDataFixture { finalized_vaa_message_emitter_chain: deposit_vaa_data.emitter_chain, finalized_vaa_message_emitter_address: deposit_vaa_data.emitter_address, finalized_vaa_message_base_fee: deposit_base_fee, - deposit_message, + vaa_payload: deposit_vaa_data.payload.to_vec(), + deposit_payload: deposit.payload.to_vec(), fast_market_order: fast_market_order.clone(), guardian_set_bump, } } - pub fn decode_cctp_message( - &self, - ) -> CctpMessageDecoded { + pub fn decode_cctp_message(&self) -> CctpMessageDecoded { let cctp_message_decoded = CctpMessage::parse(&self.encoded_cctp_message[..]).unwrap(); CctpMessageDecoded { nonce: cctp_message_decoded.nonce(), @@ -177,20 +192,23 @@ pub async fn prepare_order_response_cctp_shim( accounts: PrepareOrderResponseShimAccountsFixture, data: PrepareOrderResponseShimDataFixture, matching_engine_program_id: &Pubkey, -) -> Result<()> { +) -> Result { + let fast_market_order_digest = data.fast_market_order.digest(); let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, - &data.fast_market_order.digest + &fast_market_order_digest, ]; - let (prepared_order_response_pda, prepared_order_response_bump) = Pubkey::find_program_address(&prepared_order_response_seeds, matching_engine_program_id); - + let (prepared_order_response_pda, _prepared_order_response_bump) = + Pubkey::find_program_address(&prepared_order_response_seeds, matching_engine_program_id); + let prepared_custody_token_seeds = [ matching_engine::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, prepared_order_response_pda.as_ref(), ]; - let (prepared_custody_token_pda, prepared_custody_token_bump) = Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); - + let (prepared_custody_token_pda, _prepared_custody_token_bump) = + Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); + let ix_accounts = PrepareOrderResponseCctpShimAccounts { signer: &accounts.signer, custodian: &accounts.custodian, @@ -205,14 +223,17 @@ pub async fn prepare_order_response_cctp_shim( cctp_message_transmitter_authority: &accounts.cctp_message_transmitter_authority, cctp_message_transmitter_config: &accounts.cctp_message_transmitter_config, cctp_used_nonces: &accounts.cctp_used_nonces, - cctp_message_transmitter_event_authority: &accounts.cctp_message_transmitter_event_authority, + cctp_message_transmitter_event_authority: &accounts + .cctp_message_transmitter_event_authority, cctp_token_messenger: &accounts.cctp_token_messenger, cctp_remote_token_messenger: &accounts.cctp_remote_token_messenger, cctp_token_minter: &accounts.cctp_token_minter, cctp_local_token: &accounts.cctp_local_token, cctp_token_pair: &accounts.cctp_token_pair, - cctp_token_messenger_minter_event_authority: &accounts.cctp_token_messenger_minter_event_authority, - cctp_token_messenger_minter_custody_token: &accounts.cctp_token_messenger_minter_custody_token, + cctp_token_messenger_minter_event_authority: &accounts + .cctp_token_messenger_minter_event_authority, + cctp_token_messenger_minter_custody_token: &accounts + .cctp_token_messenger_minter_custody_token, cctp_token_messenger_minter_program: &accounts.cctp_token_messenger_minter_program, cctp_message_transmitter_program: &accounts.cctp_message_transmitter_program, guardian_set: &accounts.guardian_set, @@ -223,12 +244,9 @@ pub async fn prepare_order_response_cctp_shim( }; let finalized_vaa_message = FinalizedVaaMessage { - vaa_sequence: data.finalized_vaa_message_sequence, - vaa_timestamp: data.finalized_vaa_message_timestamp, - vaa_emitter_chain: data.finalized_vaa_message_emitter_chain, - vaa_emitter_address: data.finalized_vaa_message_emitter_address, base_fee: data.finalized_vaa_message_base_fee, - deposit_message: data.deposit_message, + vaa_payload: data.vaa_payload, + deposit_payload: data.deposit_payload, guardian_set_bump: data.guardian_set_bump, }; @@ -237,15 +255,139 @@ pub async fn prepare_order_response_cctp_shim( cctp_attestation: data.cctp_attestation, finalized_vaa_message, }; - + let prepare_order_response_cctp_shim_ix = PrepareOrderResponseCctpShimIx { program_id: matching_engine_program_id, accounts: ix_accounts, data, - }.instruction(); + } + .instruction(); + + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new latest blockhash"); + let transaction = Transaction::new_signed_with_payer( + &[prepare_order_response_cctp_shim_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + recent_blockhash, + ); + test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to process prepare order response cctp shim"); + Ok(PrepareOrderResponseShimFixture { + prepared_order_response: prepared_order_response_pda, + prepared_custody_token: prepared_custody_token_pda, + }) +} - let recent_blockhash = test_ctx.borrow_mut().get_new_latest_blockhash().await.expect("Failed to get new latest blockhash"); - let transaction = Transaction::new_signed_with_payer(&[prepare_order_response_cctp_shim_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash); - test_ctx.borrow_mut().banks_client.process_transaction(transaction).await.expect("Failed to process prepare order response cctp shim"); - Ok(()) -} \ No newline at end of file +pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { + // TODO: Fix this + let payload = deposit.payload.clone(); + let liquidity_layer_message = LiquidityLayerDepositMessage::parse(&payload).unwrap(); + let slow_order_response = liquidity_layer_message + .slow_order_response() + .expect("Failed to get slow order response"); + let base_fee = slow_order_response.base_fee(); + base_fee +} + +pub async fn prepare_order_response_test( + test_ctx: &Rc>, + payer_signer: &Rc, + deposit_vaa_data: &super::vaa::PostedVaaData, + core_bridge_program_id: &Pubkey, + matching_engine_program_id: &Pubkey, + fixture_accounts: &FixtureAccounts, + execute_order_fixture: &ExecuteOrderFallbackFixture, + initial_offer_fixture: &PlaceInitialOfferShimFixture, + initialize_fixture: &InitializeFixture, + eth_endpoint_address: &Pubkey, + arb_endpoint_address: &Pubkey, + usdc_mint_address: &Pubkey, + cctp_mint_recipient: &Pubkey, + custodian_address: &Pubkey, + deposit: &Deposit, +) -> Result { + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = + super::shims::create_guardian_signatures( + test_ctx, + payer_signer, + deposit_vaa_data, + core_bridge_program_id, + None, + ) + .await; + + let source_remote_token_messenger = super::router::get_remote_token_messenger( + test_ctx, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await; + let cctp_nonce = deposit.cctp_nonce; + println!("cctp nonce: {:?}", cctp_nonce); + + let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; + let fast_market_order_state = initial_offer_fixture.fast_market_order; + // TODO: Make checks to see if fast market order sender matches cctp message sender ... + let cctp_message_decoded = super::cctp_message::craft_cctp_token_burn_message( + test_ctx, + source_remote_token_messenger.domain, + cctp_nonce, + deposit.amount, + &message_transmitter_config_pubkey, + &(&source_remote_token_messenger).into(), + cctp_mint_recipient, + custodian_address, + ) + .await + .unwrap(); + cctp_message_decoded + .verify_cctp_message(&fast_market_order_state) + .unwrap(); + + let deposit_base_fee = super::shims_prepare_order_response::get_deposit_base_fee(&deposit); + let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new( + cctp_message_decoded.encoded_cctp_burn_message, + cctp_message_decoded.cctp_attestation, + &deposit_vaa_data, + &deposit, + deposit_base_fee, + &fast_market_order_state, + guardian_set_bump, + ); + let cctp_message_decoded = prepare_order_response_cctp_shim_data.decode_cctp_message(); + let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new( + &payer_signer.pubkey(), + &fixture_accounts, + &execute_order_fixture, + &initial_offer_fixture, + &initialize_fixture, + ð_endpoint_address, + &arb_endpoint_address, + &usdc_mint_address, + &cctp_message_decoded, + &guardian_set_pubkey, + &guardian_signatures_pubkey, + ); + let result = super::shims_prepare_order_response::prepare_order_response_cctp_shim( + test_ctx, + payer_signer, + prepare_order_response_cctp_shim_accounts, + prepare_order_response_cctp_shim_data, + matching_engine_program_id, + ) + .await; + assert!(result.is_ok()); + result +} + +pub struct PrepareOrderResponseShimFixture { + pub prepared_order_response: Pubkey, + pub prepared_custody_token: Pubkey, +} diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/programs/matching-engine/tests/utils/token_account.rs index 2c5b1c065..3ea41d4fc 100644 --- a/solana/programs/matching-engine/tests/utils/token_account.rs +++ b/solana/programs/matching-engine/tests/utils/token_account.rs @@ -1,7 +1,10 @@ -use solana_sdk::{program_pack::Pack, transaction::Transaction, pubkey::Pubkey, signature::Keypair, signer::Signer}; -use anchor_spl::token::spl_token; use anchor_spl::associated_token::spl_associated_token_account; +use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; +use solana_sdk::{ + program_pack::Pack, pubkey::Pubkey, signature::Keypair, signer::Signer, + transaction::Transaction, +}; use std::{cell::RefCell, fs, rc::Rc}; #[derive(Clone)] @@ -13,7 +16,11 @@ pub struct TokenAccountFixture { impl std::fmt::Debug for TokenAccountFixture { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "TokenAccountFixture {{ address: {}, account: {:?} }}", self.address, self.account) + write!( + f, + "TokenAccountFixture {{ address: {}, account: {:?} }}", + self.address, self.account + ) } } @@ -30,26 +37,24 @@ pub async fn create_token_account( owner: &Keypair, mint: &Pubkey, ) -> TokenAccountFixture { - let test_ctx_ref = Rc::clone(&test_ctx); // Derive the Associated Token Account (ATA) for fee_recipient - let token_account_address = spl_associated_token_account::get_associated_token_address( - &owner.pubkey(), - mint, - ); + let token_account_address = + spl_associated_token_account::get_associated_token_address(&owner.pubkey(), mint); // Inspired by https://github.com/mrgnlabs/marginfi-v2/blob/3b7bf0aceb684a762c8552412001c8d355033119/test-utils/src/spl.rs#L56 let token_account = { let mut ctx = test_ctx.borrow_mut(); - + // Create instruction using borrowed values - let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( - &ctx.payer.pubkey(), // Funding account - &owner.pubkey(), // Wallet address - mint, // Mint address - &spl_token::id(), // Token program - ); + let create_ata_ix = + spl_associated_token_account::instruction::create_associated_token_account( + &ctx.payer.pubkey(), // Funding account + &owner.pubkey(), // Wallet address + mint, // Mint address + &spl_token::id(), // Token program + ); // Create and process transaction let tx = Transaction::new_signed_with_payer( @@ -57,7 +62,7 @@ pub async fn create_token_account( Some(&ctx.payer.pubkey()), &[&ctx.payer], ctx.last_blockhash, - ); + ); ctx.banks_client.process_transaction(tx).await.unwrap(); @@ -81,16 +86,16 @@ pub async fn create_token_account_for_pda( mint: &Pubkey, // The mint (USDC in your case) ) -> Pubkey { let mut ctx = test_context.borrow_mut(); - + // Get the ATA address let ata = anchor_spl::associated_token::get_associated_token_address(&pda, mint); - + // Create the create_ata instruction let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( - &ctx.payer.pubkey(), // Funding account - pda, // Account that will own the token account - mint, // Token mint (USDC) - &spl_token::id(), // Token program + &ctx.payer.pubkey(), // Funding account + pda, // Account that will own the token account + mint, // Token mint (USDC) + &spl_token::id(), // Token program ); // Create and send transaction @@ -101,7 +106,10 @@ pub async fn create_token_account_for_pda( ctx.last_blockhash, ); - ctx.banks_client.process_transaction(transaction).await.unwrap(); + ctx.banks_client + .process_transaction(transaction) + .await + .unwrap(); ata } @@ -115,14 +123,12 @@ pub async fn create_token_account_for_pda( /// * `filename` - The path to the JSON fixture file pub fn read_keypair_from_file(filename: &str) -> Keypair { // Read the JSON file - let data = fs::read_to_string(filename) - .expect("Unable to read file"); + let data = fs::read_to_string(filename).expect("Unable to read file"); // Parse JSON array into Vec - let bytes: Vec = serde_json::from_str(&data) - .expect("File content must be a JSON array of integers"); + let bytes: Vec = + serde_json::from_str(&data).expect("File content must be a JSON array of integers"); // Create keypair from bytes - Keypair::from_bytes(&bytes) - .expect("Bytes must form a valid keypair") -} \ No newline at end of file + Keypair::from_bytes(&bytes).expect("Bytes must form a valid keypair") +} diff --git a/solana/programs/matching-engine/tests/utils/transfer_ownership.rs b/solana/programs/matching-engine/tests/utils/transfer_ownership.rs deleted file mode 100644 index 980c4dcbd..000000000 --- a/solana/programs/matching-engine/tests/utils/transfer_ownership.rs +++ /dev/null @@ -1,32 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_lang::{InstructionData, ToAccountMetas}; - -pub fn transfer_ownership( - program_id: Pubkey, - custodian: Pubkey, - cctp_mint_recipient: Pubkey, - mint: Pubkey, -) -> Instruction { - // TODO: Implement this -} - -fn create_submit_ownership_transfer_ix( - program_id: Pubkey, - custodian: Pubkey, - sender: Pubkey, - new_owner: Pubkey, -) -> Instruction { - - let accounts = matching_engine::accounts::SubmitOwnershipTransferRequest { - admin: sender, - new_owner, - }; - - let ix_data = matching_engine::instruction::SubmitOwnershipTransferRequest{}.data(); - - Instruction { - program_id, - accounts: accounts.to_account_metas(None), - data: ix_data, - } -} \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 95c0811b3..4834597ed 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -1,32 +1,30 @@ use anchor_lang::prelude::*; +use common::messages::wormhole_io::{TypePrefixedPayload, WriteableBytes}; use common::messages::{FastMarketOrder, SlowOrderResponse}; -use common::messages::wormhole_io::{WriteableBytes, TypePrefixedPayload}; use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload use secp256k1::SecretKey as SecpSecretKey; +use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use super::constants::Chain; use super::CHAIN_TO_DOMAIN; -use borsh::{ - BorshDeserialize, - BorshSerialize, -}; -use serde::{ - Deserialize, - Serialize, -}; -use solana_sdk::account::Account; use super::constants::CORE_BRIDGE_PID; -use solana_program_test::{ProgramTest, ProgramTestContext}; +use borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Deserialize, Serialize}; use solana_program::keccak; +use solana_program_test::{ProgramTest, ProgramTestContext}; +use solana_sdk::account::Account; use std::cell::RefCell; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; pub trait DataDiscriminator { const DISCRIMINATOR: &'static [u8]; } -#[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive( + Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq, Eq, +)] pub struct PostedVaaData { /// Header of the posted VAA // pub vaa_version: u8, (This is removed because it is encoded in the discriminator) @@ -57,7 +55,6 @@ pub struct PostedVaaData { /// Message payload pub payload: Vec, - } impl DataDiscriminator for PostedVaaData { @@ -66,7 +63,11 @@ impl DataDiscriminator for PostedVaaData { impl PostedVaaData { pub fn new( - chain: Chain, payload: Vec, emitter_address: impl ToBytes, sequence: u64, nonce: u32 + chain: Chain, + payload: Vec, + emitter_address: impl ToBytes, + sequence: u64, + nonce: u32, ) -> Self { let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -98,12 +99,16 @@ impl PostedVaaData { ]) } - pub fn sign_with_guardian_key(&self, guardian_secret_key: &SecpSecretKey, index: u8) -> [u8; 66] { + pub fn sign_with_guardian_key( + &self, + guardian_secret_key: &SecpSecretKey, + index: u8, + ) -> [u8; 66] { // Sign the message hash with the guardian key let secp = secp256k1::SECP256K1; let msg = secp256k1::Message::from_digest(self.digest()); let recoverable_signature = secp.sign_ecdsa_recoverable(&msg, &guardian_secret_key); - let mut signature_bytes = [0u8; 66]; + let mut signature_bytes = [0u8; GUARDIAN_SIGNATURE_LENGTH]; // First byte is the index signature_bytes[0] = index; // Next 64 bytes are the signature in compact format @@ -115,7 +120,10 @@ impl PostedVaaData { } pub fn digest(&self) -> [u8; 32] { - keccak::hashv(&[self.message_hash().as_ref()]).as_ref().try_into().unwrap() + keccak::hashv(&[self.message_hash().as_ref()]) + .as_ref() + .try_into() + .unwrap() } pub fn create_vaa_account(&self, program_test: &mut ProgramTest, vaa_address: Pubkey) { @@ -132,7 +140,9 @@ impl PostedVaaData { } } -pub fn deserialize_with_discriminator(data: &[u8]) -> Option { +pub fn deserialize_with_discriminator( + data: &[u8], +) -> Option { let mut discriminant = [0u8; 4]; discriminant.copy_from_slice(&data[..4]); if discriminant != T::DISCRIMINATOR { @@ -146,9 +156,9 @@ pub fn deserialize_with_discriminator(d } } -pub fn serialize_with_discriminator(message: &T) -> Result> +pub fn serialize_with_discriminator(message: &T) -> Result> where - T: BorshSerialize + DataDiscriminator + T: BorshSerialize + DataDiscriminator, { let mut data = Vec::new(); data.extend_from_slice(T::DISCRIMINATOR); @@ -162,12 +172,30 @@ pub enum PayloadDeserialized { FastTransfer(FastMarketOrder), } +impl PayloadDeserialized { + pub fn get_deposit(&self) -> Option { + match self { + Self::Deposit(deposit) => Some(deposit.clone()), + _ => None, + } + } + + #[allow(dead_code)] + pub fn get_fast_transfer(&self) -> Option { + match self { + Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), + _ => None, + } + } +} + #[derive(Clone)] pub struct TestVaa { pub kind: TestVaaKind, pub vaa_pubkey: Pubkey, pub vaa_data: PostedVaaData, pub payload_deserialized: Option, + pub is_posted: bool, } impl TestVaa { @@ -189,14 +217,24 @@ pub struct CreateDepositAndFastTransferParams { impl Default for CreateDepositAndFastTransferParams { fn default() -> Self { - Self { deposit_params: CreateDepositParams::default(), fast_transfer_params: CreateFastTransferParams::default() } + Self { + deposit_params: CreateDepositParams::default(), + fast_transfer_params: CreateFastTransferParams::default(), + } } } impl CreateDepositAndFastTransferParams { pub fn verify(&self) { - assert!(self.fast_transfer_params.max_fee > self.deposit_params.base_fee + self.fast_transfer_params.init_auction_fee, "Max fee must be greater than the sum of the base fee and the init auction fee"); - assert!(self.fast_transfer_params.amount_in > self.fast_transfer_params.max_fee , "Amount in must be greater than max fee"); + assert!( + self.fast_transfer_params.max_fee + > self.deposit_params.base_fee + self.fast_transfer_params.init_auction_fee, + "Max fee must be greater than the sum of the base fee and the init auction fee" + ); + assert!( + self.fast_transfer_params.amount_in > self.fast_transfer_params.max_fee, + "Amount in must be greater than max fee" + ); } } @@ -207,7 +245,10 @@ pub struct CreateDepositParams { impl Default for CreateDepositParams { fn default() -> Self { - Self { amount: 69000000, base_fee: 0 } + Self { + amount: 69000000, + base_fee: 2, + } } } @@ -220,11 +261,17 @@ pub struct CreateFastTransferParams { impl Default for CreateFastTransferParams { fn default() -> Self { - Self { amount_in: 69000000, min_amount_out: 69000000, max_fee: 6000000, init_auction_fee: 10 } + Self { + amount_in: 69000000, + min_amount_out: 69000000, + max_fee: 6000000, + init_auction_fee: 10, + } } } -pub struct TestFastTransfer { +#[derive(Clone)] +pub struct TestVaaPair { pub token_mint: Pubkey, pub source_address: ChainAddress, pub refund_address: ChainAddress, @@ -232,48 +279,133 @@ pub struct TestFastTransfer { pub cctp_nonce: u32, pub sequence: u64, pub fast_transfer_vaa: TestVaa, // kind: TestVaaKind::FastTransfer - pub deposit_vaa: TestVaa, // kind: TestVaaKind::Deposit + pub deposit_vaa: TestVaa, // kind: TestVaaKind::Deposit } -impl TestFastTransfer { - pub fn new(start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams) -> Self { +impl TestVaaPair { + pub fn new( + start_timestamp: Option, + token_mint: Pubkey, + source_address: ChainAddress, + refund_address: ChainAddress, + destination_address: ChainAddress, + cctp_nonce: u64, + vaa_nonce: u32, + sequence: u64, + cctp_mint_recipient: Pubkey, + create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, + is_posted: bool, + ) -> Self { create_deposit_and_fast_transfer_params.verify(); let deposit_params = create_deposit_and_fast_transfer_params.deposit_params; - let create_fast_transfer_params = create_deposit_and_fast_transfer_params.fast_transfer_params; - let (deposit_vaa_pubkey, deposit_vaa_data, deposit) = create_deposit_message(token_mint, source_address.clone(), destination_address.clone(), cctp_nonce, sequence, cctp_mint_recipient, deposit_params.amount, deposit_params.base_fee); - let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data, fast_market_order) = create_fast_transfer_message(start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), cctp_nonce, sequence, create_fast_transfer_params.amount_in, create_fast_transfer_params.min_amount_out, create_fast_transfer_params.max_fee, create_fast_transfer_params.init_auction_fee); - Self { token_mint, source_address, refund_address, destination_address, cctp_nonce:cctp_nonce as u32, sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, vaa_pubkey: deposit_vaa_pubkey, vaa_data: deposit_vaa_data, payload_deserialized: Some(PayloadDeserialized::Deposit(deposit)) }, fast_transfer_vaa: TestVaa { kind: TestVaaKind::FastTransfer, vaa_pubkey: fast_transfer_vaa_pubkey, vaa_data: fast_transfer_vaa_data, payload_deserialized: Some(PayloadDeserialized::FastTransfer(fast_market_order)) } } + let create_fast_transfer_params = + create_deposit_and_fast_transfer_params.fast_transfer_params; + let (deposit_vaa_pubkey, deposit_vaa_data, deposit) = create_deposit_message( + token_mint, + source_address.clone(), + destination_address.clone(), + cctp_nonce, + sequence, + cctp_mint_recipient, + deposit_params.amount, + deposit_params.base_fee, + vaa_nonce, + ); + let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data, fast_market_order) = + create_fast_transfer_message( + start_timestamp, + source_address.clone(), + refund_address.clone(), + destination_address.clone(), + vaa_nonce, + sequence + 1, + create_fast_transfer_params.amount_in, + create_fast_transfer_params.min_amount_out, + create_fast_transfer_params.max_fee, + create_fast_transfer_params.init_auction_fee, + ); + Self { + token_mint, + source_address, + refund_address, + destination_address, + cctp_nonce: cctp_nonce as u32, + sequence, + deposit_vaa: TestVaa { + kind: TestVaaKind::Deposit, + vaa_pubkey: deposit_vaa_pubkey, + vaa_data: deposit_vaa_data, + payload_deserialized: Some(PayloadDeserialized::Deposit(deposit)), + is_posted, + }, + fast_transfer_vaa: TestVaa { + kind: TestVaaKind::FastTransfer, + vaa_pubkey: fast_transfer_vaa_pubkey, + vaa_data: fast_transfer_vaa_data, + payload_deserialized: Some(PayloadDeserialized::FastTransfer(fast_market_order)), + is_posted, + }, + } } - pub fn add_to_test(&self, program_test:&mut ProgramTest) { - self.deposit_vaa.vaa_data.create_vaa_account(program_test, self.deposit_vaa.vaa_pubkey.clone()); - self.fast_transfer_vaa.vaa_data.create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey.clone()); + pub fn add_to_test(&self, program_test: &mut ProgramTest) { + self.deposit_vaa + .vaa_data + .create_vaa_account(program_test, self.deposit_vaa.vaa_pubkey.clone()); + self.fast_transfer_vaa + .vaa_data + .create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey.clone()); } - pub async fn verify_vaas(&self, test_context: &Rc>) { + pub async fn verify_posted_vaa_pair(&self, test_context: &Rc>) { let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); { - let deposit_vaa = test_context.borrow_mut().banks_client.get_account(self.deposit_vaa.vaa_pubkey.clone()).await.unwrap(); + let deposit_vaa = test_context + .borrow_mut() + .banks_client + .get_account(self.deposit_vaa.vaa_pubkey.clone()) + .await + .unwrap(); assert!(deposit_vaa.is_some(), "Deposit VAA not found"); - let deposit_vaa = deserialize_with_discriminator::(&deposit_vaa.unwrap().data).unwrap(); + let deposit_vaa = + deserialize_with_discriminator::(&deposit_vaa.unwrap().data) + .unwrap(); assert_eq!(deposit_vaa, expected_deposit_vaa); } { - let fast_transfer_vaa = test_context.borrow_mut().banks_client.get_account(self.fast_transfer_vaa.vaa_pubkey.clone()).await.unwrap(); + let fast_transfer_vaa = test_context + .borrow_mut() + .banks_client + .get_account(self.fast_transfer_vaa.vaa_pubkey.clone()) + .await + .unwrap(); assert!(fast_transfer_vaa.is_some(), "Fast transfer VAA not found"); - let fast_transfer_vaa = deserialize_with_discriminator::(&fast_transfer_vaa.unwrap().data).unwrap(); + let fast_transfer_vaa = + deserialize_with_discriminator::(&fast_transfer_vaa.unwrap().data) + .unwrap(); assert_eq!(fast_transfer_vaa, expected_fast_transfer_vaa); } } + + pub fn is_posted(&self) -> bool { + self.deposit_vaa.is_posted && self.fast_transfer_vaa.is_posted + } } -pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, amount: i32, base_fee: u64) -> (Pubkey, PostedVaaData, Deposit) { - - let slow_order_response = SlowOrderResponse { - base_fee, - }; +pub fn create_deposit_message( + token_mint: Pubkey, + source_address: ChainAddress, + destination_address: ChainAddress, + cctp_nonce: u64, + sequence: u64, + cctp_mint_recipient: Pubkey, + amount: i32, + base_fee: u64, + vaa_nonce: u32, +) -> (Pubkey, PostedVaaData, Deposit) { + let slow_order_response = SlowOrderResponse { base_fee }; // Implements TypePrefixedPayload let deposit = Deposit { token_address: token_mint.to_bytes(), @@ -289,14 +421,32 @@ pub fn create_deposit_message(token_mint: Pubkey, source_address: ChainAddress, // TODO: Checks on deposit // Sequece == nonce in this case, since only vaas we are submitting are fast transfers - let posted_vaa_data = PostedVaaData::new(source_address.chain, deposit.to_vec(), source_address.address, sequence, cctp_nonce as u32); + let posted_vaa_data = PostedVaaData::new( + source_address.chain, + deposit.to_vec(), + source_address.address, + sequence, + vaa_nonce as u32, + ); let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); - let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; + let vaa_address = + Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; (vaa_address, posted_vaa_data, deposit) } -pub fn create_fast_transfer_message(start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, amount_in: u64, min_amount_out: u64, max_fee: u64, init_auction_fee: u64) -> (Pubkey, PostedVaaData, FastMarketOrder) { +pub fn create_fast_transfer_message( + start_timestamp: Option, + source_address: ChainAddress, + refund_address: ChainAddress, + destination_address: ChainAddress, + vaa_nonce: u32, + sequence: u64, + amount_in: u64, + min_amount_out: u64, + max_fee: u64, + init_auction_fee: u64, +) -> (Pubkey, PostedVaaData, FastMarketOrder) { // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); // Implements TypePrefixedPayload @@ -307,79 +457,168 @@ pub fn create_fast_transfer_message(start_timestamp: Option, source_address redeemer: destination_address.address.to_bytes(), sender: source_address.address.to_bytes(), refund_address: refund_address.address.to_bytes(), // Not used so can be all zeros - max_fee, // USDC max fee + max_fee, // USDC max fee init_auction_fee, // USDC init auction fee (the first person to verify a vaa and start an auction will get this fee) so at least rent - deadline, // If dealine is 0 then there is no deadline + deadline, // If dealine is 0 then there is no deadline redeemer_message: WriteableBytes::new(vec![]), }; // TODO: Checks on fast transfer - let posted_vaa_data = PostedVaaData::new(source_address.chain, fast_market_order.to_vec(), source_address.address, sequence, cctp_nonce as u32); + let posted_vaa_data = PostedVaaData::new( + source_address.chain, + fast_market_order.to_vec(), + source_address.address, + sequence, + vaa_nonce, + ); let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); - let vaa_address = Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; + let vaa_address = + Pubkey::find_program_address(&[b"PostedVAA", vaa_hash_as_slice], &CORE_BRIDGE_PID).0; (vaa_address, posted_vaa_data, fast_market_order) } -pub struct TestFastTransfers(pub Vec); +pub struct TestVaaPairs(pub Vec); -impl TestFastTransfers { - pub fn new() -> Self { - Self(Vec::new()) +impl Deref for TestVaaPairs { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 } +} - #[allow(dead_code)] - pub fn len(&self) -> usize { - self.0.len() +impl DerefMut for TestVaaPairs { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } +} - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.0.is_empty() +impl TestVaaPairs { + pub fn new() -> Self { + Self(Vec::new()) } /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer - pub fn add_ft(&mut self, start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, cctp_mint_recipient: Pubkey, sequence: Option, nonce: Option, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams) { - let sequence = sequence.unwrap_or(self.len() as u64); - let cctp_nonce = nonce.unwrap_or(sequence); - let test_fast_transfer = TestFastTransfer::new(start_timestamp, token_mint, source_address, refund_address, destination_address, cctp_nonce, sequence, cctp_mint_recipient, create_deposit_and_fast_transfer_params); + pub fn add_ft( + &mut self, + token_mint: Pubkey, + source_address: ChainAddress, + refund_address: ChainAddress, + destination_address: ChainAddress, + cctp_mint_recipient: Pubkey, + vaa_args: &VaaArgs, + create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, + ) { + let sequence = vaa_args.sequence.unwrap_or(self.len() as u64); + let cctp_nonce = vaa_args.cctp_nonce.unwrap_or(sequence + 1); + let vaa_nonce = vaa_args.vaa_nonce.unwrap_or(0); + let is_posted = vaa_args.post_vaa; + let test_fast_transfer = TestVaaPair::new( + vaa_args.start_timestamp, + token_mint, + source_address, + refund_address, + destination_address, + cctp_nonce, + vaa_nonce, + sequence, + cctp_mint_recipient, + create_deposit_and_fast_transfer_params, + is_posted, + ); self.0.push(test_fast_transfer); } - pub fn create_vaas_with_chain_and_address(&mut self, program_test: &mut ProgramTest, mint_address: Pubkey, start_timestamp: Option, cctp_mint_recipient: Pubkey, source_chain: Chain, destination_chain: Chain, source_address: [u8; 32], destination_address: [u8; 32], sequence: Option, nonce: Option, create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, add_fast_transfer_to_test: bool) { + pub fn create_vaas_with_chain_and_address( + &mut self, + program_test: &mut ProgramTest, + mint_address: Pubkey, + cctp_mint_recipient: Pubkey, + source_chain: Chain, + destination_chain: Chain, + source_address: [u8; 32], + destination_address: [u8; 32], + vaa_args: &VaaArgs, + create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, + ) { let source_address = ChainAddress::new_with_address(source_chain, source_address); - let destination_address = ChainAddress::new_with_address(destination_chain, destination_address); + let destination_address = + ChainAddress::new_with_address(destination_chain, destination_address); let refund_address = source_address.clone(); - self.add_ft(start_timestamp, mint_address.clone(), source_address, refund_address, destination_address, cctp_mint_recipient, sequence, nonce, create_deposit_and_fast_transfer_params); - if add_fast_transfer_to_test { + self.add_ft( + mint_address.clone(), + source_address, + refund_address, + destination_address, + cctp_mint_recipient, + vaa_args, + create_deposit_and_fast_transfer_params, + ); + if vaa_args.post_vaa { for test_fast_transfer in self.0.iter() { test_fast_transfer.add_to_test(program_test); } } } + + pub async fn verify_posted_vaas(&self, test_context: &Rc>) { + for vaa_pair in self.0.iter() { + if vaa_pair.is_posted() { + vaa_pair.verify_posted_vaa_pair(test_context).await; + } + } + } +} + +pub struct VaaArgs { + pub sequence: Option, + pub cctp_nonce: Option, + pub vaa_nonce: Option, + pub start_timestamp: Option, + pub post_vaa: bool, +} + +impl Default for VaaArgs { + fn default() -> Self { + Self { + sequence: None, + cctp_nonce: None, + vaa_nonce: None, + start_timestamp: None, + post_vaa: false, + } + } } pub fn create_vaas_test_with_chain_and_address( - program_test: &mut ProgramTest, - mint_address: Pubkey, - start_timestamp: Option, - cctp_mint_recipient: Pubkey, - source_chain: Chain, - destination_chain: Chain, - source_address: [u8; 32], + program_test: &mut ProgramTest, + mint_address: Pubkey, + cctp_mint_recipient: Pubkey, + source_chain: Chain, + destination_chain: Chain, + source_address: [u8; 32], destination_address: [u8; 32], - sequence: Option, - nonce: Option, - add_fast_transfer_to_test: bool, -) -> TestFastTransfers { - let mut test_fast_transfers = TestFastTransfers::new(); + vaa_args: VaaArgs, +) -> TestVaaPairs { + let mut test_fast_transfers = TestVaaPairs::new(); let create_deposit_and_fast_transfer_params = CreateDepositAndFastTransferParams::default(); - test_fast_transfers.create_vaas_with_chain_and_address(program_test, mint_address, start_timestamp, cctp_mint_recipient, source_chain, destination_chain, source_address, destination_address, sequence, nonce, create_deposit_and_fast_transfer_params, add_fast_transfer_to_test); + test_fast_transfers.create_vaas_with_chain_and_address( + program_test, + mint_address, + cctp_mint_recipient, + source_chain, + destination_chain, + source_address, + destination_address, + &vaa_args, + create_deposit_and_fast_transfer_params, + ); test_fast_transfers } pub trait ToBytes { - fn to_bytes(&self) ->[u8; 32]; + fn to_bytes(&self) -> [u8; 32]; } #[allow(dead_code)] @@ -408,7 +647,7 @@ impl EvmAddress { pub fn new(bytes: [u8; 20]) -> Self { Self(bytes) } - + pub fn from_hex(hex: &str) -> Option { let hex = hex.strip_prefix("0x").unwrap_or(hex); let bytes = hex::decode(hex).ok()?; @@ -419,17 +658,18 @@ impl EvmAddress { array.copy_from_slice(&bytes); Some(Self(array)) } - + pub fn as_bytes(&self) -> &[u8; 20] { &self.0 } - + pub fn to_hex(&self) -> String { format!("0x{}", hex::encode(self.0)) } pub fn new_unique() -> Self { - let (_secp_secret_key, secp_pubkey) = secp256k1::generate_keypair(&mut secp256k1::rand::rngs::OsRng); + let (_secp_secret_key, secp_pubkey) = + secp256k1::generate_keypair(&mut secp256k1::rand::rngs::OsRng); // Get uncompressed public key bytes (65 bytes: prefix + x + y) let uncompressed = secp_pubkey.serialize_uncompressed(); // Hash with Keccak-256 removing the prefix @@ -459,19 +699,42 @@ impl ChainAddress { #[allow(dead_code)] pub fn new_unique(chain: Chain) -> Self { match chain { - Chain::Solana => Self { chain, address: TestPubkey::Solana(Pubkey::new_unique()) }, - Chain::Ethereum => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, - Chain::Arbitrum => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, - Chain::Avalanche => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, - Chain::Optimism => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, - Chain::Polygon => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, - Chain::Base => Self { chain, address: TestPubkey::Evm(EvmAddress::new_unique()) }, + Chain::Solana => Self { + chain, + address: TestPubkey::Solana(Pubkey::new_unique()), + }, + Chain::Ethereum => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Arbitrum => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Avalanche => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Optimism => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Polygon => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Base => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, } } #[allow(dead_code)] pub fn new_with_address(chain: Chain, address: [u8; 32]) -> Self { - Self { chain, address: TestPubkey::Bytes(address) } + Self { + chain, + address: TestPubkey::Bytes(address), + } } } - From 25f3e7d7534ab2da8a9550eaf6b182945c31c26a Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 20 Mar 2025 16:27:31 +0000 Subject: [PATCH 022/112] [broken] execute order shim does not check auction deadline --- .../src/fallback/processor/execute_order.rs | 2 + .../tests/initialize_integration_tests.rs | 409 +++++++++++------- .../matching-engine/tests/shimful/README.md | 4 + .../matching-engine/tests/shimful/mod.rs | 3 + .../tests/{utils => shimful}/shims.rs | 97 +++-- .../{utils => shimful}/shims_execute_order.rs | 91 +++- .../shims_prepare_order_response.rs | 18 +- .../matching-engine/tests/shimless/README.md | 4 + .../tests/shimless/execute_order.rs | 194 +++++++++ .../tests/{utils => shimless}/initialize.rs | 0 .../tests/shimless/make_offer.rs | 248 +++++++++++ .../matching-engine/tests/shimless/mod.rs | 4 + .../{utils => shimless}/settle_auction.rs | 8 +- .../matching-engine/tests/utils/auction.rs | 254 ++--------- .../matching-engine/tests/utils/mod.rs | 5 - .../matching-engine/tests/utils/setup.rs | 6 + 16 files changed, 891 insertions(+), 456 deletions(-) create mode 100644 solana/programs/matching-engine/tests/shimful/README.md create mode 100644 solana/programs/matching-engine/tests/shimful/mod.rs rename solana/programs/matching-engine/tests/{utils => shimful}/shims.rs (90%) rename solana/programs/matching-engine/tests/{utils => shimful}/shims_execute_order.rs (77%) rename solana/programs/matching-engine/tests/{utils => shimful}/shims_prepare_order_response.rs (97%) create mode 100644 solana/programs/matching-engine/tests/shimless/README.md create mode 100644 solana/programs/matching-engine/tests/shimless/execute_order.rs rename solana/programs/matching-engine/tests/{utils => shimless}/initialize.rs (100%) create mode 100644 solana/programs/matching-engine/tests/shimless/make_offer.rs create mode 100644 solana/programs/matching-engine/tests/shimless/mod.rs rename solana/programs/matching-engine/tests/{utils => shimless}/settle_auction.rs (87%) diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 3769f699c..ab10e22f1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -274,6 +274,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let mut active_auction = Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; + // TODO: Check that the auction has reached its deadline + // Correct way to use create_program_address with existing seeds and bump let active_auction_pda = Pubkey::create_program_address( &[ diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 78d92b1ca..30194ced0 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -2,23 +2,26 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; use matching_engine::state::FastMarketOrder; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; +use shimless::execute_order::execute_order_shimless_test; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; +mod shimful; +mod shimless; mod utils; -use solana_sdk::signer::Signer; +use shimful::shims::{ + initialise_fast_market_order_fallback_instruction, place_initial_offer_fallback, + place_initial_offer_fallback_test, set_up_post_message_transaction_test, +}; +use shimful::shims_execute_order::execute_order_fallback_test; +use shimless::initialize::initialize_program; +use shimless::make_offer::{improve_offer, place_initial_offer_shimless}; use solana_sdk::transaction::VersionedTransaction; -use utils::auction::{improve_offer, place_initial_offer, AuctionAccounts}; -use utils::initialize::initialize_program; +use utils::auction::AuctionAccounts; use utils::router::{ add_local_router_endpoint_ix, create_all_router_endpoints_test, create_cctp_router_endpoints_test, }; use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; -use utils::shims::{ - initialise_fast_market_order_fallback_instruction, place_initial_offer_fallback, - set_up_post_message_transaction_test, -}; -use utils::shims_execute_order::{execute_order_fallback, ExecuteOrderFallbackAccounts}; use utils::vaa::VaaArgs; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; @@ -132,7 +135,14 @@ pub async fn test_setup_vaas() { transfer_direction, ); - place_initial_offer(&mut testing_context, auction_accounts, fast_vaa, PROGRAM_ID).await; + place_initial_offer_shimless( + &mut testing_context, + &auction_accounts, + fast_vaa, + PROGRAM_ID, + true, // Expected to pass + ) + .await; let auction_state = testing_context .testing_state .auction_state @@ -193,9 +203,9 @@ pub async fn test_initialise_fast_market_order_fallback() { let vaa_data = first_test_ft.vaa_data; let (fast_market_order, vaa_data) = - utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + shimful::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - utils::shims::create_guardian_signatures( + shimful::shims::create_guardian_signatures( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, @@ -247,9 +257,9 @@ pub async fn test_close_fast_market_order_fallback() { let vaa_data = first_test_ft.vaa_data; let (fast_market_order, vaa_data) = - utils::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); + shimful::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - utils::shims::create_guardian_signatures( + shimful::shims::create_guardian_signatures( &testing_context.test_context, &testing_context.testing_actors.owner.keypair(), &vaa_data, @@ -299,7 +309,7 @@ pub async fn test_close_fast_market_order_fallback() { &PROGRAM_ID, ) .0; - utils::shims::close_fast_market_order_fallback( + shimful::shims::close_fast_market_order_fallback( &testing_context.test_context, &solver.keypair(), &PROGRAM_ID, @@ -358,7 +368,7 @@ pub async fn test_approve_usdc() { // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = - utils::shims::create_guardian_signatures( + shimful::shims::create_guardian_signatures( &testing_context.test_context, &actors.owner.keypair(), &vaa_data, @@ -399,47 +409,20 @@ pub async fn test_place_initial_offer_fallback() { .await; let initialize_fixture = initialize_program(&testing_context).await; - - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - - // Try making initial offer using the shim instruction - let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_endpoints = create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + None, ) .await; - - let solver = testing_context.testing_actors.solvers[0].clone(); - let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey - transfer_direction, - ); - - let vaa_data = first_test_ft.vaa_data; - - // Place initial offer using the fallback program - let payer_signer = testing_context.testing_actors.owner.keypair(); - let _initial_offer_fixture = place_initial_offer_fallback( + let _initial_offer_fixture = place_initial_offer_fallback_test( &mut testing_context, - &payer_signer, - &PROGRAM_ID, - &CORE_BRIDGE_PROGRAM_ID, - &vaa_data, - solver.clone(), &auction_accounts, - 1__000_000, // 1 USDC (double underscore for decimal separator) + true, // Expected to pass ) - .await - .expect("Failed to place initial offer"); + .await; // Attempt to improve the offer using the non-fallback method with another solver making the improved offer println!("Improving offer"); let second_solver = testing_context.testing_actors.solvers[1].clone(); @@ -455,12 +438,10 @@ pub async fn test_place_initial_offer_fallback() { } #[tokio::test] -// Testing an execute order from arbitrum to ethereum -// TODO: Flesh out this test to see if the message was posted correctly -pub async fn test_execute_order_fallback() { +pub async fn test_place_initial_offer_shim_blocks_non_shim() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = VaaArgs { - post_vaa: false, + post_vaa: true, ..VaaArgs::default() }; let mut testing_context = setup_environment( @@ -471,80 +452,215 @@ pub async fn test_execute_order_fallback() { .await; let initialize_fixture = initialize_program(&testing_context).await; - + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + None, + ) + .await; + let initial_offer_fallback_fixture = place_initial_offer_fallback_test( + &mut testing_context, + &auction_accounts, // Auction accounts have not been created yet + true, // Expected to pass + ) + .await + .expect("Should have been able to place initial offer"); let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - - let fixture_accounts = testing_context - .get_fixture_accounts() - .expect("Pre-made fixture accounts not found"); - // Try making initial offer using the shim instruction - let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_endpoints = create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), + // Now test without the fallback program + let mut auction_accounts = initial_offer_fallback_fixture.auction_accounts; + auction_accounts.fast_vaa = Some(first_test_ft.get_vaa_pubkey()); + place_initial_offer_shimless( + &mut testing_context, + &auction_accounts, + first_test_ft, + PROGRAM_ID, + false, // Expected to fail ) .await; +} - let solver = testing_context.testing_actors.solvers[0].clone(); - let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey +#[tokio::test] +pub async fn test_place_initial_offer_non_shim_blocks_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, transfer_direction, - ); + Some(vaa_args), + ) + .await; - let vaa_data = first_test_ft.vaa_data; + let initialize_fixture = initialize_program(&testing_context).await; + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + Some(first_test_ft.get_vaa_pubkey()), + ) + .await; + // Place initial offer using the shimless instruction + place_initial_offer_shimless( + &mut testing_context, + &auction_accounts, + first_test_ft, + PROGRAM_ID, + true, // Expected to pass + ) + .await; + // Now test with the fallback program (shims) and expect it to fail + let none_initial_offer_fallback_fixture = place_initial_offer_fallback_test( + &mut testing_context, + &auction_accounts, + false, // Expected to fail + ) + .await; + assert!(none_initial_offer_fallback_fixture.is_none()); +} - let payer_signer = testing_context.testing_actors.owner.keypair(); +#[tokio::test] +// Testing an execute order from arbitrum to ethereum +// TODO: Flesh out this test to see if the message was posted correctly +pub async fn test_execute_order_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; - // Place initial offer using the fallback program - let initial_offer_fixture = place_initial_offer_fallback( + let initialize_fixture = initialize_program(&testing_context).await; + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + None, + ) + .await; + let initial_offer_fallback_fixture = place_initial_offer_fallback_test( &mut testing_context, - &payer_signer, - &PROGRAM_ID, - &CORE_BRIDGE_PROGRAM_ID, - &vaa_data, - solver.clone(), &auction_accounts, - 1__000_000, // 1 USDC (double underscore for decimal separator) + true, // Expected to pass ) .await - .expect("Failed to place initial offer"); + .expect("Should have been able to place initial offer"); + let solver = testing_context.testing_actors.solvers[0].clone(); + let balance_before_execute_order = solver.get_balance(&testing_context.test_context).await; println!( "Solver balance after placing initial offer: {:?}", - solver.get_balance(&testing_context.test_context).await + balance_before_execute_order ); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + let _execute_order_fixture = execute_order_fallback_test( + &mut testing_context, &auction_accounts, - &initial_offer_fixture, - &payer_signer.pubkey(), - &fixture_accounts, - transfer_direction, + &initial_offer_fallback_fixture, + solver.clone(), + true, // Expected to pass + ) + .await + .expect("Failed to execute order"); + + let balance_after_execute_order = solver.get_balance(&testing_context.test_context).await; + assert!( + balance_after_execute_order > balance_before_execute_order, + "Solver balance after executing order was {:?}, but should have been greater", + balance_after_execute_order ); +} + +#[tokio::test] +pub async fn test_execute_order_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let initialize_fixture = initialize_program(&testing_context).await; + + let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let first_test_fast_transfer_pubkey = first_test_fast_transfer.get_vaa_pubkey(); + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + Some(first_test_fast_transfer_pubkey), + ) + .await; + place_initial_offer_shimless( + &mut testing_context, + &auction_accounts, + first_test_fast_transfer, + PROGRAM_ID, + true, // Expected to pass + ) + .await; + + let execute_order_fixture = + execute_order_shimless_test(&mut testing_context, &auction_accounts, true).await; + assert!(execute_order_fixture.is_some()); +} +pub async fn test_execute_order_fallback_blocks_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let mut testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let initialize_fixture = initialize_program(&testing_context).await; + let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + Some(first_test_fast_transfer.get_vaa_pubkey()), + ) + .await; + let initial_offer_fallback_fixture = place_initial_offer_fallback_test( + &mut testing_context, + &auction_accounts, + true, // Expected to pass + ) + .await + .expect("Should have been able to place initial offer"); + + let solver = testing_context.testing_actors.solvers[0].clone(); + // Try executing the order using the fallback program - let _execute_order_fixture = execute_order_fallback( - &testing_context.test_context, - &payer_signer, - &PROGRAM_ID, + let _shim_execute_order_fixture = execute_order_fallback_test( + &mut testing_context, + &auction_accounts, + &initial_offer_fallback_fixture, solver.clone(), - &execute_order_fallback_accounts, + true, // Expected to pass ) .await .expect("Failed to execute order"); - // Figure out why the solver balance is not increased here - println!( - "Solver balance after executing order: {:?}", - solver.get_balance(&testing_context.test_context).await - ); + let shimless_execute_order_fixture = + execute_order_shimless_test(&mut testing_context, &auction_accounts, false).await; + assert!(shimless_execute_order_fixture.is_none()); } // From ethereum to arbitrum @@ -561,7 +677,6 @@ pub async fn test_prepare_order_shim_fallback() { Some(vaa_args), ) .await; - let initialize_fixture = initialize_program(&testing_context).await; let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); @@ -575,71 +690,43 @@ pub async fn test_prepare_order_shim_fallback() { let fixture_accounts = testing_context .get_fixture_accounts() .expect("Pre-made fixture accounts not found"); + // Try making initial offer using the shim instruction let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_endpoints = create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), + let auction_accounts = AuctionAccounts::create_auction_accounts( + &mut testing_context, + &initialize_fixture, + transfer_direction, + None, ) .await; - let solver = testing_context.testing_actors.solvers[0].clone(); - let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey - transfer_direction, - ); - - let fast_transfer_vaa_data = first_vaa_pair.fast_transfer_vaa.vaa_data; - let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; - - let payer_signer = testing_context.testing_actors.owner.keypair(); - // Place initial offer using the fallback program - let initial_offer_fixture = place_initial_offer_fallback( + let initial_offer_fixture = place_initial_offer_fallback_test( &mut testing_context, - &payer_signer, - &PROGRAM_ID, - &CORE_BRIDGE_PROGRAM_ID, - &fast_transfer_vaa_data, - solver.clone(), &auction_accounts, - 1__000_000, // 1 USDC (double underscore for decimal separator) + true, // Expected to pass ) .await .expect("Failed to place initial offer"); - println!( - "Solver balance after placing initial offer: {:?}", - solver.get_balance(&testing_context.test_context).await - ); + let solver = testing_context.testing_actors.solvers[0].clone(); + + let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; + + let payer_signer = testing_context.testing_actors.owner.keypair(); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + let execute_order_fixture = execute_order_fallback_test( + &mut testing_context, &auction_accounts, &initial_offer_fixture, - &payer_signer.pubkey(), - &fixture_accounts, - transfer_direction, - ); - // Try executing the order using the fallback program - let execute_order_fixture = execute_order_fallback( - &testing_context.test_context, - &payer_signer, - &PROGRAM_ID, solver.clone(), - &execute_order_fallback_accounts, + true, // Expected to pass ) .await .expect("Failed to execute order"); - utils::shims_prepare_order_response::prepare_order_response_test( + shimful::shims_prepare_order_response::prepare_order_response_test( &testing_context.test_context, &payer_signer, &deposit_vaa_data, @@ -649,8 +736,8 @@ pub async fn test_prepare_order_shim_fallback() { &execute_order_fixture, &initial_offer_fixture, &initialize_fixture, - &router_endpoints.ethereum.endpoint_address, - &router_endpoints.arbitrum.endpoint_address, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, &usdc_mint_address, &CCTP_MINT_RECIPIENT, &initialize_fixture.get_custodian_address(), @@ -725,6 +812,7 @@ pub async fn test_settle_auction_complete() { solver.clone(), &auction_accounts, 1__000_000, // 1 USDC (double underscore for decimal separator) + true, ) .await .expect("Failed to place initial offer"); @@ -734,26 +822,17 @@ pub async fn test_settle_auction_complete() { solver.get_balance(&testing_context.test_context).await ); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + let execute_order_fixture = execute_order_fallback_test( + &mut testing_context, &auction_accounts, &initial_offer_fixture, - &payer_signer.pubkey(), - &fixture_accounts, - transfer_direction, - ); - // Try executing the order using the fallback program - let execute_order_fixture = execute_order_fallback( - &testing_context.test_context, - &payer_signer, - &PROGRAM_ID, solver.clone(), - &execute_order_fallback_accounts, + true, // Expected to pass ) .await .expect("Failed to execute order"); - let prepare_order_response_shim_fixture = - utils::shims_prepare_order_response::prepare_order_response_test( + shimful::shims_prepare_order_response::prepare_order_response_test( &testing_context.test_context, &payer_signer, &deposit_vaa_data, @@ -773,7 +852,7 @@ pub async fn test_settle_auction_complete() { .await .expect("Failed to prepare order response"); let auction_state = initial_offer_fixture.auction_state; - utils::settle_auction::settle_auction_complete( + shimless::settle_auction::settle_auction_complete( &testing_context.test_context, &payer_signer, &usdc_mint_address, diff --git a/solana/programs/matching-engine/tests/shimful/README.md b/solana/programs/matching-engine/tests/shimful/README.md new file mode 100644 index 000000000..bf885461c --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/README.md @@ -0,0 +1,4 @@ +# Shimful Tests + +This directory contains tests that use the fallback program. + diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs new file mode 100644 index 000000000..63aaef9a4 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -0,0 +1,3 @@ +pub mod shims; +pub mod shims_execute_order; +pub mod shims_prepare_order_response; diff --git a/solana/programs/matching-engine/tests/utils/shims.rs b/solana/programs/matching-engine/tests/shimful/shims.rs similarity index 90% rename from solana/programs/matching-engine/tests/utils/shims.rs rename to solana/programs/matching-engine/tests/shimful/shims.rs index 9fccadea4..d3345286b 100644 --- a/solana/programs/matching-engine/tests/utils/shims.rs +++ b/solana/programs/matching-engine/tests/shimful/shims.rs @@ -1,5 +1,6 @@ -use super::setup::TestingContext; -use super::{constants::*, setup::Solver}; +use super::super::utils; +use super::super::utils::setup::TestingContext; +use super::super::utils::{constants::*, setup::Solver}; use anchor_lang::prelude::*; use base64::Engine; use common::messages::FastMarketOrder; @@ -410,12 +411,39 @@ fn generate_expected_guardian_signatures_info( (expected_length, guardian_signatures) } +// TODO: Separate this into a different file pub struct PlaceInitialOfferShimFixture { - pub auction_state: Rc>, + pub auction_state: Rc>, pub guardian_set_pubkey: Pubkey, pub guardian_signatures_pubkey: Pubkey, pub fast_market_order_address: Pubkey, pub fast_market_order: FastMarketOrderState, + pub auction_accounts: utils::auction::AuctionAccounts, +} + +pub async fn place_initial_offer_fallback_test( + testing_context: &mut TestingContext, + auction_accounts: &utils::auction::AuctionAccounts, + expected_to_pass: bool, +) -> Option { + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let solver = testing_context.testing_actors.solvers[0].clone(); + let vaa_data = first_test_ft.vaa_data; + + // Place initial offer using the fallback program + let payer_signer = testing_context.testing_actors.owner.keypair(); + place_initial_offer_fallback( + testing_context, + &payer_signer, + &testing_context.get_matching_engine_program_id(), + &testing_context.get_wormhole_program_id(), + &vaa_data, + solver.clone(), + auction_accounts, + 1__000_000, // 1 USDC (double underscore for decimal separator) + expected_to_pass, + ) + .await } /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. @@ -424,11 +452,12 @@ pub async fn place_initial_offer_fallback( payer_signer: &Rc, program_id: &Pubkey, wormhole_program_id: &Pubkey, - vaa_data: &super::vaa::PostedVaaData, + vaa_data: &utils::vaa::PostedVaaData, solver: Solver, - auction_accounts: &super::auction::AuctionAccounts, + auction_accounts: &utils::auction::AuctionAccounts, offer_price: u64, -) -> Result { + expected_to_pass: bool, +) -> Option { let test_ctx = &testing_context.test_context; let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); @@ -528,36 +557,38 @@ pub async fn place_initial_offer_fallback( recent_blockhash, ); - test_ctx + let tx_result = test_ctx .borrow_mut() .banks_client .process_transaction(transaction) - .await - .expect("Failed to place initial offer"); - - testing_context.testing_state.auction_state = - super::auction::AuctionState::Active(super::auction::ActiveAuctionState { + .await; + assert_eq!(tx_result.is_ok(), expected_to_pass); + if tx_result.is_ok() { + let new_active_auction_state = utils::auction::ActiveAuctionState { auction_address, auction_custody_token_address, - best_offer: super::auction::AuctionOffer { - best_offer_token: auction_accounts.offer_token, - best_offer_price: offer_price, + initial_offer: utils::auction::AuctionOffer { + offer_token: auction_accounts.offer_token, + offer_price, }, - }); - Ok(PlaceInitialOfferShimFixture { - auction_state: Rc::new(RefCell::new(super::auction::ActiveAuctionState { - auction_address, - auction_custody_token_address, - best_offer: super::auction::AuctionOffer { - best_offer_token: auction_accounts.offer_token, - best_offer_price: offer_price, + best_offer: utils::auction::AuctionOffer { + offer_token: auction_accounts.offer_token, + offer_price, }, - })), - guardian_set_pubkey, - guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), - fast_market_order_address: fast_market_order_account, - fast_market_order, - }) + }; + testing_context.testing_state.auction_state = + utils::auction::AuctionState::Active(new_active_auction_state.clone()); + Some(PlaceInitialOfferShimFixture { + auction_state: Rc::new(RefCell::new(new_active_auction_state)), + guardian_set_pubkey, + guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), + fast_market_order_address: fast_market_order_account, + fast_market_order, + auction_accounts: auction_accounts.clone(), + }) + } else { + None + } } pub fn initialise_fast_market_order_fallback_instruction( @@ -630,10 +661,10 @@ pub async fn close_fast_market_order_fallback( } pub fn create_fast_market_order_state_from_vaa_data( - vaa_data: &super::vaa::PostedVaaData, + vaa_data: &utils::vaa::PostedVaaData, refund_recipient: Pubkey, -) -> (FastMarketOrderState, super::vaa::PostedVaaData) { - let vaa_data = super::vaa::PostedVaaData { +) -> (FastMarketOrderState, utils::vaa::PostedVaaData) { + let vaa_data = utils::vaa::PostedVaaData { consistency_level: vaa_data.consistency_level, vaa_time: vaa_data.vaa_time, sequence: vaa_data.sequence, @@ -700,7 +731,7 @@ pub fn create_fast_market_order_state_from_vaa_data( pub async fn create_guardian_signatures( test_ctx: &Rc>, payer_signer: &Rc, - vaa_data: &super::vaa::PostedVaaData, + vaa_data: &utils::vaa::PostedVaaData, wormhole_program_id: &Pubkey, guardian_signature_signer: Option<&Rc>, ) -> (Pubkey, Pubkey, u8) { diff --git a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs similarity index 77% rename from solana/programs/matching-engine/tests/utils/shims_execute_order.rs rename to solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index f64c05b89..132c56cc0 100644 --- a/solana/programs/matching-engine/tests/utils/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -1,6 +1,7 @@ -use super::setup::TransferDirection; -use super::{constants::*, setup::Solver}; -use anchor_lang::prelude::*; +use crate::utils::setup::TestingContext; + +use super::super::utils; +use super::shims; use anchor_spl::token::spl_token; use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, @@ -12,6 +13,8 @@ use solana_sdk::{ }; use std::cell::RefCell; use std::rc::Rc; +use utils::setup::TransferDirection; +use utils::{constants::*, setup::Solver}; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; use wormhole_svm_definitions::{ solana::{ @@ -22,6 +25,7 @@ use wormhole_svm_definitions::{ }; pub struct ExecuteOrderFallbackAccounts { + pub signer: Pubkey, pub custodian: Pubkey, pub fast_market_order_address: Pubkey, pub active_auction: Pubkey, @@ -37,10 +41,10 @@ pub struct ExecuteOrderFallbackAccounts { impl ExecuteOrderFallbackAccounts { pub fn new( - auction_accounts: &super::auction::AuctionAccounts, - place_initial_offer_fixture: &super::shims::PlaceInitialOfferShimFixture, + auction_accounts: &utils::auction::AuctionAccounts, + place_initial_offer_fixture: &shims::PlaceInitialOfferShimFixture, // Does not need to be place initial offer fixture, can just be fast market order address. Auction state and transfer direction can be taken from testing context signer: &Pubkey, - fixture_accounts: &super::account_fixtures::FixtureAccounts, + fixture_accounts: &utils::account_fixtures::FixtureAccounts, transfer_direction: TransferDirection, ) -> Self { let remote_token_messenger = match transfer_direction { @@ -52,6 +56,7 @@ impl ExecuteOrderFallbackAccounts { } }; Self { + signer: signer.clone(), custodian: auction_accounts.custodian, fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, active_auction: place_initial_offer_fixture @@ -94,7 +99,8 @@ pub async fn execute_order_fallback( program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, -) -> Result { + expected_to_pass: bool, +) -> Option { // Get target chain and use as remote address let cctp_message = Pubkey::find_program_address( &[ @@ -179,31 +185,68 @@ pub async fn execute_order_fallback( // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; - super::setup::fast_forward_slots(test_ctx, 1).await; - println!("Fast forwarded 1 slots"); + utils::setup::fast_forward_slots(test_ctx, 1).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash, ); - test_ctx + let transaction_result = test_ctx .borrow_mut() .banks_client .process_transaction(transaction) - .await - .expect("Failed to execute order"); + .await; + if expected_to_pass { + assert!( + transaction_result.is_ok(), + "Transaction should have been successful" + ); + Some(ExecuteOrderFallbackFixture { + cctp_message, + post_message_sequence, + post_message_message, + accounts: ExecuteOrderFallbackFixtureAccounts { + local_token, + token_messenger, + remote_token_messenger, + token_messenger_minter_sender_authority, + token_messenger_minter_event_authority: *token_messenger_minter_event_authority, + }, + }) + } else { + assert!( + transaction_result.is_err(), + "Transaction should have been unsuccessful" + ); + None + } +} - Ok(ExecuteOrderFallbackFixture { - cctp_message, - post_message_sequence, - post_message_message, - accounts: ExecuteOrderFallbackFixtureAccounts { - local_token, - token_messenger, - remote_token_messenger, - token_messenger_minter_sender_authority, - token_messenger_minter_event_authority: *token_messenger_minter_event_authority, - }, - }) +pub async fn execute_order_fallback_test( + testing_context: &mut TestingContext, + auction_accounts: &utils::auction::AuctionAccounts, + place_initial_offer_fixture: &shims::PlaceInitialOfferShimFixture, + solver: Solver, + expected_to_pass: bool, +) -> Option { + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Pre-made fixture accounts not found"); + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + auction_accounts, + place_initial_offer_fixture, + &testing_context.testing_actors.owner.pubkey(), + &fixture_accounts, + testing_context.testing_state.transfer_direction, + ); + execute_order_fallback( + &testing_context.test_context, + &testing_context.testing_actors.owner.keypair(), + &testing_context.get_matching_engine_program_id(), + solver, + &execute_order_fallback_accounts, + expected_to_pass, + ) + .await } diff --git a/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs similarity index 97% rename from solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs rename to solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs index 928bd1c18..72907fc85 100644 --- a/solana/programs/matching-engine/tests/utils/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs @@ -1,3 +1,7 @@ +use super::super::shimless::initialize::InitializeFixture; +use super::super::utils; +use super::shims::PlaceInitialOfferShimFixture; +use super::shims_execute_order::ExecuteOrderFallbackFixture; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use common::messages::raw::LiquidityLayerDepositMessage; @@ -17,13 +21,9 @@ use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; use std::cell::RefCell; use std::rc::Rc; +use utils::account_fixtures::FixtureAccounts; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; -use super::account_fixtures::FixtureAccounts; -use super::initialize::InitializeFixture; -use super::shims::PlaceInitialOfferShimFixture; -use super::shims_execute_order::ExecuteOrderFallbackFixture; - pub struct PrepareOrderResponseShimAccountsFixture { pub signer: Pubkey, pub custodian: Pubkey, @@ -157,7 +157,7 @@ impl PrepareOrderResponseShimDataFixture { pub fn new( encoded_cctp_message: Vec, cctp_attestation: Vec, - deposit_vaa_data: &super::vaa::PostedVaaData, + deposit_vaa_data: &utils::vaa::PostedVaaData, deposit: &Deposit, deposit_base_fee: u64, fast_market_order: &FastMarketOrderState, @@ -300,7 +300,7 @@ pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { pub async fn prepare_order_response_test( test_ctx: &Rc>, payer_signer: &Rc, - deposit_vaa_data: &super::vaa::PostedVaaData, + deposit_vaa_data: &utils::vaa::PostedVaaData, core_bridge_program_id: &Pubkey, matching_engine_program_id: &Pubkey, fixture_accounts: &FixtureAccounts, @@ -324,7 +324,7 @@ pub async fn prepare_order_response_test( ) .await; - let source_remote_token_messenger = super::router::get_remote_token_messenger( + let source_remote_token_messenger = utils::router::get_remote_token_messenger( test_ctx, fixture_accounts.ethereum_remote_token_messenger, ) @@ -335,7 +335,7 @@ pub async fn prepare_order_response_test( let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; let fast_market_order_state = initial_offer_fixture.fast_market_order; // TODO: Make checks to see if fast market order sender matches cctp message sender ... - let cctp_message_decoded = super::cctp_message::craft_cctp_token_burn_message( + let cctp_message_decoded = utils::cctp_message::craft_cctp_token_burn_message( test_ctx, source_remote_token_messenger.domain, cctp_nonce, diff --git a/solana/programs/matching-engine/tests/shimless/README.md b/solana/programs/matching-engine/tests/shimless/README.md new file mode 100644 index 000000000..2e8466673 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimless/README.md @@ -0,0 +1,4 @@ +# Shimless Tests + +This directory contains tests that do not use the fallback program. + diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs new file mode 100644 index 000000000..5c0cd210f --- /dev/null +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -0,0 +1,194 @@ +use crate::utils::account_fixtures::FixtureAccounts; +use crate::utils::auction::AuctionAccounts; +use crate::utils::setup::{TestingContext, TransferDirection}; +use anchor_lang::prelude::*; +use anchor_lang::{InstructionData, ToAccountMetas}; +use common::wormhole_cctp_solana::cctp::{ + MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, +}; +use matching_engine::accounts::{CctpDepositForBurn, WormholePublishMessage}; +use matching_engine::accounts::{ + ExecuteFastOrderCctp as ExecuteOrderShimlessAccounts, LiquidityLayerVaa, LiveRouterEndpoint, + RequiredSysvars, +}; +use matching_engine::instruction::ExecuteFastOrderCctp as ExecuteOrderShimlessInstruction; +use solana_sdk::instruction::Instruction; +use solana_sdk::sysvar::SysvarId; +use solana_sdk::transaction::Transaction; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +pub struct ExecuteOrderShimlessFixture {} + +pub fn create_execute_order_shimless_accounts( + testing_context: &mut TestingContext, + fixture_accounts: &FixtureAccounts, + auction_accounts: &AuctionAccounts, +) -> ExecuteOrderShimlessAccounts { + let active_auction_state = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap(); + let active_auction_address = active_auction_state.auction_address; + let active_auction_custody_token = active_auction_state.auction_custody_token_address; + let cctp_message = Pubkey::find_program_address( + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + &active_auction_address.to_bytes(), + ], + &testing_context.get_matching_engine_program_id(), + ) + .0; + let to_router_endpoint = LiveRouterEndpoint { + endpoint: auction_accounts.to_router_endpoint, + }; + // TODO: FIGURE out how to get this + let emitter_sequence = wormhole_svm_definitions::find_emitter_sequence_address( + &auction_accounts.custodian, + &wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, + ) + .0; + let checked_custodian = matching_engine::accounts::CheckedCustodian { + custodian: auction_accounts.custodian, + }; + let wormhole_publish_message = WormholePublishMessage { + config: wormhole_svm_definitions::solana::CORE_BRIDGE_CONFIG, + emitter_sequence: emitter_sequence, + fee_collector: wormhole_svm_definitions::solana::CORE_BRIDGE_FEE_COLLECTOR, + core_bridge_program: wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, + }; + let fast_vaa = LiquidityLayerVaa { + vaa: auction_accounts.fast_vaa.unwrap(), + }; + let active_auction = matching_engine::accounts::ActiveAuction { + auction: active_auction_address, + custody_token: active_auction_custody_token, + config: auction_accounts.auction_config, + best_offer_token: active_auction_state.best_offer.offer_token, + }; + let execute_order = matching_engine::accounts::ExecuteOrder { + fast_vaa, + active_auction, + executor_token: active_auction_state.best_offer.offer_token, // TODO: Is this correct? + initial_participant: testing_context.testing_actors.owner.pubkey(), + initial_offer_token: active_auction_state.initial_offer.offer_token, + }; + let core_message = Pubkey::find_program_address( + &[ + common::CORE_MESSAGE_SEED_PREFIX, + &active_auction_address.to_bytes(), + ], + &testing_context.get_matching_engine_program_id(), + ) + .0; + let sysvars = RequiredSysvars { + clock: Clock::id(), + rent: Rent::id(), + }; + let token_messenger_minter_event_authority = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let local_token = Pubkey::find_program_address( + &[ + b"local_token", + &testing_context.get_usdc_mint_address().to_bytes(), + ], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let token_messenger_minter_sender_authority = + Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let message_transmitter_config = + Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let token_messenger = + Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let remote_token_messenger = match testing_context.testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + fixture_accounts.arbitrum_remote_token_messenger + } + TransferDirection::FromArbitrumToEthereum => { + fixture_accounts.ethereum_remote_token_messenger + } + }; + let token_minter = + Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let cctp = CctpDepositForBurn { + mint: testing_context.get_usdc_mint_address(), + local_token, + token_messenger_minter_sender_authority, + message_transmitter_config, + token_messenger, + remote_token_messenger, + token_minter, + token_messenger_minter_event_authority, + message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, + token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, + }; + + let event_authority = Pubkey::find_program_address( + &[EVENT_AUTHORITY_SEED], + &testing_context.get_matching_engine_program_id(), + ) + .0; + ExecuteOrderShimlessAccounts { + payer: testing_context.testing_actors.owner.pubkey(), + core_message, + cctp_message, + to_router_endpoint, + custodian: checked_custodian, + execute_order, + wormhole: wormhole_publish_message, + cctp, + system_program: solana_program::system_program::ID, + token_program: anchor_spl::token::spl_token::ID, + event_authority, + program: testing_context.get_matching_engine_program_id(), + sysvars, + } +} + +pub async fn execute_order_shimless_test( + testing_context: &mut TestingContext, + auction_accounts: &AuctionAccounts, + expected_to_pass: bool, +) -> Option { + crate::utils::setup::fast_forward_slots(&testing_context.test_context, 3).await; + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Fixture accounts not found"); + let execute_order_accounts: ExecuteOrderShimlessAccounts = + create_execute_order_shimless_accounts( + testing_context, + &fixture_accounts, + auction_accounts, + ); + let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); + let execute_order_ix = Instruction { + program_id: testing_context.get_matching_engine_program_id(), + accounts: execute_order_accounts.to_account_metas(None), + data: execute_order_instruction_data, + }; + let tx = Transaction::new_signed_with_payer( + &[execute_order_ix], + Some(&testing_context.testing_actors.owner.pubkey()), + &[&testing_context.testing_actors.owner.keypair()], + testing_context + .test_context + .borrow_mut() + .get_new_latest_blockhash() + .await + .unwrap(), + ); + let tx_result = testing_context + .test_context + .borrow_mut() + .banks_client + .process_transaction(tx) + .await; + if expected_to_pass { + assert!(tx_result.is_ok()); + Some(ExecuteOrderShimlessFixture {}) + } else { + assert!(tx_result.is_err()); + None + } +} diff --git a/solana/programs/matching-engine/tests/utils/initialize.rs b/solana/programs/matching-engine/tests/shimless/initialize.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/initialize.rs rename to solana/programs/matching-engine/tests/shimless/initialize.rs diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs new file mode 100644 index 000000000..b16b190bb --- /dev/null +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -0,0 +1,248 @@ +use super::super::utils; +use anchor_lang::prelude::*; +use anchor_lang::InstructionData; + +use common::TRANSFER_AUTHORITY_SEED_PREFIX; +use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; +use matching_engine::accounts::{ + ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, + LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc, +}; +use matching_engine::instruction::{ + ImproveOffer as ImproveOfferIx, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, +}; +use matching_engine::state::Auction; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::Signer; +use solana_sdk::transaction::Transaction; +use utils::auction::{ActiveAuctionState, AuctionAccounts, AuctionOffer, AuctionState}; +use utils::setup::{Solver, TestingContext}; +use utils::vaa::TestVaa; + +pub async fn place_initial_offer_shimless( + testing_context: &mut TestingContext, + accounts: &AuctionAccounts, + fast_market_order: TestVaa, + program_id: Pubkey, + expected_to_pass: bool, +) { + let test_ctx = &testing_context.test_context; + let owner_keypair = testing_context.testing_actors.owner.keypair(); + let auction_address = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], + &program_id, + ) + .0; + let auction_custody_token_address = Pubkey::find_program_address( + &[ + matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_address.as_ref(), + ], + &program_id, + ) + .0; + let initial_offer_ix = PlaceInitialOfferCctpIx { + offer_price: 1__000_000, + }; + + let fast_order_path = FastOrderPath { + fast_vaa: LiquidityLayerVaa { + vaa: fast_market_order.vaa_pubkey, + }, + path: LiveRouterPath { + from_endpoint: LiveRouterEndpoint { + endpoint: accounts.from_router_endpoint, + }, + to_endpoint: LiveRouterEndpoint { + endpoint: accounts.to_router_endpoint, + }, + }, + }; + + let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; + let transfer_authority = Pubkey::find_program_address( + &[ + TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &initial_offer_ix.offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + { + // Check if solver has already approved usdc + let usdc_account = accounts.solver.token_account_address().unwrap(); + let usdc_account_info = test_ctx + .borrow_mut() + .banks_client + .get_account(usdc_account) + .await + .unwrap() + .unwrap(); + let token_account_info = anchor_spl::token::TokenAccount::try_deserialize( + &mut usdc_account_info.data.as_slice(), + ) + .expect("Failed to deserialize usdc account"); + if token_account_info.delegate.is_none() { + accounts + .solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; + } else { + let delegate = token_account_info.delegate.unwrap(); + if delegate != transfer_authority { + accounts + .solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; + } + } + } + + let custodian = CheckedCustodian { + custodian: accounts.custodian, + }; + let initial_offer_accounts = PlaceInitialOfferCctpAccounts { + payer: owner_keypair.pubkey(), + transfer_authority, + custodian, + auction_config: accounts.auction_config, + fast_order_path, + auction: auction_address, + offer_token: accounts.offer_token, + auction_custody_token: auction_custody_token_address, + usdc: Usdc { + mint: accounts.usdc_mint, + }, + system_program: anchor_lang::system_program::ID, + token_program: anchor_spl::token::ID, + program: program_id, + event_authority, + }; + + let mut account_metas = initial_offer_accounts.to_account_metas(None); + for meta in account_metas.iter_mut() { + if meta.pubkey == accounts.offer_token { + meta.is_writable = true; + } + } + + let initial_offer_ix_anchor = Instruction { + program_id: program_id, + accounts: account_metas, + data: initial_offer_ix.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[initial_offer_ix_anchor], + Some(&owner_keypair.pubkey()), + &[&owner_keypair], + test_ctx.borrow().last_blockhash, + ); + + let tx_result = test_ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await; + assert_eq!(tx_result.is_ok(), expected_to_pass); + if tx_result.is_ok() { + testing_context.testing_state.auction_state = AuctionState::Active(ActiveAuctionState { + auction_address, + auction_custody_token_address, + initial_offer: AuctionOffer { + offer_token: accounts.offer_token, + offer_price: initial_offer_ix.offer_price, + }, + best_offer: AuctionOffer { + offer_token: accounts.offer_token, + offer_price: initial_offer_ix.offer_price, + }, + }); + }; +} + +pub async fn improve_offer( + testing_context: &mut TestingContext, + program_id: Pubkey, + solver: Solver, + auction_config: Pubkey, +) { + let test_ctx = &testing_context.test_context; + let owner_keypair = testing_context.testing_actors.owner.keypair(); + let auction_state = &mut testing_context + .testing_state + .auction_state + .get_active_auction_mut() + .unwrap(); + let auction_address = auction_state.auction_address; + let auction_custody_token_address = auction_state.auction_custody_token_address; + + // Decrease the offer by 0.5 usdc + let improve_offer_ix = ImproveOfferIx { + offer_price: auction_state.best_offer.offer_price - 500_000, + }; + + let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; + let transfer_authority = Pubkey::find_program_address( + &[ + TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &improve_offer_ix.offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; + let offer_token = solver.token_account_address().unwrap(); + + let active_auction = ActiveAuction { + auction: auction_address, + custody_token: auction_custody_token_address, + config: auction_config, + best_offer_token: auction_state.best_offer.offer_token, + }; + let improve_offer_accounts = ImproveOfferAccounts { + transfer_authority, + active_auction, + offer_token, + token_program: anchor_spl::token::ID, + event_authority, + program: program_id, + }; + + let mut account_metas = improve_offer_accounts.to_account_metas(None); + for meta in account_metas.iter_mut() { + if meta.pubkey == auction_state.best_offer.offer_token { + meta.is_writable = true; + } + } + + // TODO: Figure out better name for this + let improve_offer_ix_anchor = Instruction { + program_id: program_id, + accounts: account_metas, + data: improve_offer_ix.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[improve_offer_ix_anchor], + Some(&owner_keypair.pubkey()), + &[&owner_keypair], + test_ctx.borrow().last_blockhash, + ); + + test_ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await + .expect("Failed to improve offer"); + + auction_state.best_offer = AuctionOffer { + offer_token, + offer_price: improve_offer_ix.offer_price, + }; +} diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs new file mode 100644 index 000000000..17104af95 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -0,0 +1,4 @@ +pub mod execute_order; +pub mod initialize; +pub mod make_offer; +pub mod settle_auction; diff --git a/solana/programs/matching-engine/tests/utils/settle_auction.rs b/solana/programs/matching-engine/tests/shimless/settle_auction.rs similarity index 87% rename from solana/programs/matching-engine/tests/utils/settle_auction.rs rename to solana/programs/matching-engine/tests/shimless/settle_auction.rs index f7c9ed577..b7cd81211 100644 --- a/solana/programs/matching-engine/tests/utils/settle_auction.rs +++ b/solana/programs/matching-engine/tests/shimless/settle_auction.rs @@ -1,3 +1,5 @@ +use super::super::shimful::*; +use super::super::utils; use anchor_lang::prelude::*; use anchor_lang::InstructionData; use anchor_spl::token::spl_token; @@ -15,8 +17,8 @@ pub async fn settle_auction_complete( test_ctx: &Rc>, payer_signer: &Rc, usdc_mint_address: &Pubkey, - prepare_order_response_shim_fixture: &super::shims_prepare_order_response::PrepareOrderResponseShimFixture, - auction_state: &Rc>, + prepare_order_response_shim_fixture: &shims_prepare_order_response::PrepareOrderResponseShimFixture, + auction_state: &Rc>, matching_engine_program_id: &Pubkey, ) -> Result<()> { let base_fee_token = usdc_mint_address.clone(); @@ -29,7 +31,7 @@ pub async fn settle_auction_complete( prepared_order_response: prepare_order_response_shim_fixture.prepared_order_response, prepared_custody_token: prepare_order_response_shim_fixture.prepared_custody_token, auction: auction_state.borrow().auction_address, - best_offer_token: auction_state.borrow().best_offer.best_offer_token, + best_offer_token: auction_state.borrow().best_offer.offer_token, token_program: spl_token::ID, event_authority: event_authority, program: *matching_engine_program_id, diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 745fef1fe..dbe965124 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,26 +1,14 @@ use anchor_lang::prelude::*; +use super::super::shimless; use super::router::TestRouterEndpoints; -use super::setup::{Solver, TestingContext, TransferDirection}; -use super::vaa::TestVaa; -use anchor_lang::InstructionData; -use common::TRANSFER_AUTHORITY_SEED_PREFIX; -use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; -use matching_engine::accounts::{ - ActiveAuction, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, LiveRouterEndpoint, - LiveRouterPath, PlaceInitialOfferCctp as PlaceInitialOfferCctpAccounts, Usdc, -}; -use matching_engine::instruction::{ - ImproveOffer as ImproveOfferIx, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, -}; +use super::setup::{Solver, TransferDirection}; use matching_engine::state::{Auction, AuctionInfo}; use solana_program_test::ProgramTestContext; -use solana_sdk::instruction::Instruction; -use solana_sdk::signature::Signer; -use solana_sdk::transaction::Transaction; use std::cell::RefCell; use std::rc::Rc; +#[derive(Clone)] pub struct AuctionAccounts { pub fast_vaa: Option, pub offer_token: Pubkey, @@ -52,15 +40,18 @@ impl AuctionState { } } } +#[derive(Clone)] pub struct ActiveAuctionState { pub auction_address: Pubkey, pub auction_custody_token_address: Pubkey, + pub initial_offer: AuctionOffer, pub best_offer: AuctionOffer, } +#[derive(Clone)] pub struct AuctionOffer { - pub best_offer_token: Pubkey, - pub best_offer_price: u64, + pub offer_token: Pubkey, + pub offer_price: u64, } impl AuctionAccounts { @@ -94,6 +85,34 @@ impl AuctionAccounts { usdc_mint, } } + + pub async fn create_auction_accounts( + testing_context: &mut super::setup::TestingContext, + initialize_fixture: &shimless::initialize::InitializeFixture, + transfer_direction: TransferDirection, + fast_vaa_pubkey: Option, + ) -> Self { + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_endpoints = super::router::create_all_router_endpoints_test( + &testing_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + testing_context.testing_actors.owner.keypair(), + ) + .await; + + let solver = testing_context.testing_actors.solvers[0].clone(); + Self::new( + fast_vaa_pubkey, // Fast VAA pubkey + solver.clone(), // Solver + auction_config_address.clone(), // Auction config pubkey + &router_endpoints, // Router endpoints + initialize_fixture.get_custodian_address(), // Custodian pubkey + usdc_mint_address, // USDC mint pubkey + transfer_direction, + ) + } } impl ActiveAuctionState { @@ -125,10 +144,7 @@ impl ActiveAuctionState { destination_asset_info: None, }; assert_eq!(auction_info.config_id, expected_auction_info.config_id); - assert_eq!( - auction_info.vaa_sequence, - expected_auction_info.vaa_sequence - ); + assert_eq!( auction_info.source_chain, expected_auction_info.source_chain @@ -146,199 +162,3 @@ impl ActiveAuctionState { ); } } - -pub async fn place_initial_offer( - testing_context: &mut TestingContext, - accounts: AuctionAccounts, - fast_market_order: TestVaa, - program_id: Pubkey, -) { - let test_ctx = &testing_context.test_context; - let owner_keypair = testing_context.testing_actors.owner.keypair(); - let auction_address = Pubkey::find_program_address( - &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], - &program_id, - ) - .0; - let auction_custody_token_address = Pubkey::find_program_address( - &[ - matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction_address.as_ref(), - ], - &program_id, - ) - .0; - let initial_offer_ix = PlaceInitialOfferCctpIx { - offer_price: 1__000_000, - }; - - let fast_order_path = FastOrderPath { - fast_vaa: LiquidityLayerVaa { - vaa: fast_market_order.vaa_pubkey, - }, - path: LiveRouterPath { - from_endpoint: LiveRouterEndpoint { - endpoint: accounts.from_router_endpoint, - }, - to_endpoint: LiveRouterEndpoint { - endpoint: accounts.to_router_endpoint, - }, - }, - }; - - let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; - let transfer_authority = Pubkey::find_program_address( - &[ - TRANSFER_AUTHORITY_SEED_PREFIX, - &auction_address.to_bytes(), - &initial_offer_ix.offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; - accounts - .solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) - .await; - let custodian = CheckedCustodian { - custodian: accounts.custodian, - }; - let initial_offer_accounts = PlaceInitialOfferCctpAccounts { - payer: owner_keypair.pubkey(), - transfer_authority, - custodian, - auction_config: accounts.auction_config, - fast_order_path, - auction: auction_address, - offer_token: accounts.offer_token, - auction_custody_token: auction_custody_token_address, - usdc: Usdc { - mint: accounts.usdc_mint, - }, - system_program: anchor_lang::system_program::ID, - token_program: anchor_spl::token::ID, - program: program_id, - event_authority, - }; - - let mut account_metas = initial_offer_accounts.to_account_metas(None); - for meta in account_metas.iter_mut() { - if meta.pubkey == accounts.offer_token { - meta.is_writable = true; - } - } - - let initial_offer_ix_anchor = Instruction { - program_id: program_id, - accounts: account_metas, - data: initial_offer_ix.data(), - }; - - let tx = Transaction::new_signed_with_payer( - &[initial_offer_ix_anchor], - Some(&owner_keypair.pubkey()), - &[&owner_keypair], - test_ctx.borrow().last_blockhash, - ); - - test_ctx - .borrow_mut() - .banks_client - .process_transaction(tx) - .await - .expect("Failed to place initial offer"); - - testing_context.testing_state.auction_state = AuctionState::Active(ActiveAuctionState { - auction_address, - auction_custody_token_address, - best_offer: AuctionOffer { - best_offer_token: accounts.offer_token, - best_offer_price: initial_offer_ix.offer_price, - }, - }); -} - -pub async fn improve_offer( - testing_context: &mut TestingContext, - program_id: Pubkey, - solver: Solver, - auction_config: Pubkey, -) { - let test_ctx = &testing_context.test_context; - let owner_keypair = testing_context.testing_actors.owner.keypair(); - let auction_state = &mut testing_context - .testing_state - .auction_state - .get_active_auction_mut() - .unwrap(); - let auction_address = auction_state.auction_address; - let auction_custody_token_address = auction_state.auction_custody_token_address; - - // Decrease the offer by 0.5 usdc - let improve_offer_ix = ImproveOfferIx { - offer_price: auction_state.best_offer.best_offer_price - 500_000, - }; - - let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; - let transfer_authority = Pubkey::find_program_address( - &[ - TRANSFER_AUTHORITY_SEED_PREFIX, - &auction_address.to_bytes(), - &improve_offer_ix.offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; - solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) - .await; - let offer_token = solver.token_account_address().unwrap(); - - let active_auction = ActiveAuction { - auction: auction_address, - custody_token: auction_custody_token_address, - config: auction_config, - best_offer_token: auction_state.best_offer.best_offer_token, - }; - let improve_offer_accounts = ImproveOfferAccounts { - transfer_authority, - active_auction, - offer_token, - token_program: anchor_spl::token::ID, - event_authority, - program: program_id, - }; - - let mut account_metas = improve_offer_accounts.to_account_metas(None); - for meta in account_metas.iter_mut() { - if meta.pubkey == auction_state.best_offer.best_offer_token { - meta.is_writable = true; - } - } - - // TODO: Figure out better name for this - let improve_offer_ix_anchor = Instruction { - program_id: program_id, - accounts: account_metas, - data: improve_offer_ix.data(), - }; - - let tx = Transaction::new_signed_with_payer( - &[improve_offer_ix_anchor], - Some(&owner_keypair.pubkey()), - &[&owner_keypair], - test_ctx.borrow().last_blockhash, - ); - - test_ctx - .borrow_mut() - .banks_client - .process_transaction(tx) - .await - .expect("Failed to improve offer"); - - auction_state.best_offer = AuctionOffer { - best_offer_token: offer_token, - best_offer_price: improve_offer_ix.offer_price, - }; -} diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index 9599caf9c..bfe48fdb6 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -3,15 +3,10 @@ pub mod airdrop; pub mod auction; pub mod cctp_message; pub mod constants; -pub mod initialize; pub mod mint; pub mod program_fixtures; pub mod router; -pub mod settle_auction; pub mod setup; -pub mod shims; -pub mod shims_execute_order; -pub mod shims_prepare_order_response; pub mod token_account; pub mod vaa; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index e92f5a6c9..e47c18a84 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -211,6 +211,10 @@ impl TestingContext { pub fn get_cctp_mint_recipient(&self) -> Pubkey { CCTP_MINT_RECIPIENT } + + pub fn get_wormhole_program_id(&self) -> Pubkey { + wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID + } } #[derive(Clone)] @@ -456,6 +460,8 @@ pub async fn fast_forward_slots(test_context: &Rc>, .process_transaction(tx) .await .expect("Failed to process transaction after warping"); + + println!("Fast forwarded {} slots", num_slots); } pub enum ShimMode { From 093c29df7793a0ccf54b1035c6132b1082da4663 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Sun, 23 Mar 2025 18:39:42 +0000 Subject: [PATCH 023/112] fixed stack issue with prepare order response --- solana/programs/matching-engine/README.md | 10 + solana/programs/matching-engine/src/error.rs | 14 ++ .../processor/close_fast_market_order.rs | 45 ++-- .../src/fallback/processor/errors.rs | 35 --- .../src/fallback/processor/execute_order.rs | 28 ++- .../{create_account.rs => helpers.rs} | 10 + .../processor/initialise_fast_market_order.rs | 61 ++++- .../src/fallback/processor/mod.rs | 4 +- .../fallback/processor/place_initial_offer.rs | 20 +- .../processor/prepare_order_response.rs | 174 +++++-------- .../fallback/processor/process_instruction.rs | 24 +- .../src/state/fast_market_order.rs | 30 ++- .../tests/initialize_integration_tests.rs | 179 +++++++------ .../matching-engine/tests/shimful/shims.rs | 7 +- .../tests/shimful/shims_execute_order.rs | 2 +- .../tests/shimless/initialize.rs | 130 ++++++---- .../tests/shimless/make_offer.rs | 52 ++-- .../tests/testing_engine/config.rs | 82 ++++++ .../tests/testing_engine/engine.rs | 236 +++++++++++++++++ .../tests/testing_engine/mod.rs | 3 + .../tests/testing_engine/state.rs | 0 .../matching-engine/tests/utils/auction.rs | 5 +- .../matching-engine/tests/utils/mod.rs | 2 + .../matching-engine/tests/utils/setup.rs | 137 ++++++++-- .../tests/utils/testing_engine.rs | 237 ++++++++++++++++++ .../tests/utils/testing_engine_configs.rs | 82 ++++++ .../matching-engine/tests/utils/vaa.rs | 15 +- 27 files changed, 1227 insertions(+), 397 deletions(-) delete mode 100644 solana/programs/matching-engine/src/fallback/processor/errors.rs rename solana/programs/matching-engine/src/fallback/processor/{create_account.rs => helpers.rs} (95%) create mode 100644 solana/programs/matching-engine/tests/testing_engine/config.rs create mode 100644 solana/programs/matching-engine/tests/testing_engine/engine.rs create mode 100644 solana/programs/matching-engine/tests/testing_engine/mod.rs create mode 100644 solana/programs/matching-engine/tests/testing_engine/state.rs create mode 100644 solana/programs/matching-engine/tests/utils/testing_engine.rs create mode 100644 solana/programs/matching-engine/tests/utils/testing_engine_configs.rs diff --git a/solana/programs/matching-engine/README.md b/solana/programs/matching-engine/README.md index 895eb3b25..4f5174d21 100644 --- a/solana/programs/matching-engine/README.md +++ b/solana/programs/matching-engine/README.md @@ -2,3 +2,13 @@ A program to facilitate the transfer of USDC between networks that allow Wormhole and CCTP bridging. With the help of solvers, allowing USDC to be transferred faster than finality. + +## Testing plan + +The testing engine should be designed in a functional way that allows for easy testing of the program instructions. + +The instructions passed to the testing engine should be able to be composed in a way where each instruction returns the updated state (not a mutating state). + +This state is predictable and has the benefit of being able to be tested in isolation and mocked (to an extent) for testing. + + diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index 847815f02..4b54bc9fe 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -89,6 +89,20 @@ pub enum MatchingEngineError { AuctionHistoryFull = 0x504, InvalidVerifyVaaShimProgram = 0x600, + + // Fallback matching engine errors + AccountAlreadyInitialized = 0x700, + AccountNotWritable = 0x702, + BorshDeserializationError = 0x704, + InvalidPda = 0x706, + AccountDataTooSmall = 0x708, + InvalidProgram = 0x70a, + TokenTransferFailed = 0x70c, + InvalidMint = 0x70e, + + #[msg("From and to router endpoints are the same")] + SameEndpoints = 0x800, + InvalidCctpMessage = 0x802, } #[cfg(test)] diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 6142e0e9b..e635a1ca0 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -3,18 +3,22 @@ use anchor_lang::prelude::*; use solana_program::instruction::Instruction; use solana_program::program_error::ProgramError; +use super::helpers::check_account_length; use super::FallbackMatchingEngineInstruction; pub struct CloseFastMarketOrderAccounts<'ix> { + /// The fast market order account created from the initialise fast market order instruction pub fast_market_order: &'ix Pubkey, - pub refund_recipient: &'ix Pubkey, + /// The account that will receive the refund. CHECK: Must be a signer. + /// CHECK: Must match the close account refund recipient in the fast market order account + pub close_account_refund_recipient: &'ix Pubkey, } impl<'ix> CloseFastMarketOrderAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ AccountMeta::new(*self.fast_market_order, false), - AccountMeta::new(*self.refund_recipient, false), + AccountMeta::new(*self.close_account_refund_recipient, true), ] } } @@ -34,32 +38,43 @@ impl CloseFastMarketOrder<'_> { } } +/// Closes the fast market order and transfers the lamports from the fast market order to the close account refund recipient +/// +/// # Arguments +/// +/// * `accounts` - The accounts of the fast market order and the close account refund recipient +/// +/// # Returns +/// +/// Result<()> pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { - if accounts.len() < 2 { - return Err(ProgramError::NotEnoughAccountKeys.into()); - } + check_account_length(accounts, 2)?; let fast_market_order = &accounts[0]; - let refund_recipient = &accounts[1]; + let close_account_refund_recipient = &accounts[1]; - if !refund_recipient.is_signer { + if !close_account_refund_recipient.is_signer { msg!("Refund recipient (account #2) is not a signer"); return Err(ProgramError::InvalidAccountData.into()); } let fast_market_order_data = FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; - if fast_market_order_data.refund_recipient != refund_recipient.key().as_ref() { - msg!("Refund recipient (account #2) mismatch"); - msg!("Actual:"); - msg!("{:?}", refund_recipient.key.as_ref()); - msg!("Expected:"); - msg!("{:?}", fast_market_order_data.refund_recipient); - return Err(ProgramError::InvalidAccountData.into()); + if fast_market_order_data.close_account_refund_recipient + != close_account_refund_recipient.key().as_ref() + { + return Err(ProgramError::InvalidAccountData.into()).map_err(|e: Error| { + e.with_pubkeys(( + Pubkey::try_from(fast_market_order_data.close_account_refund_recipient) + .expect("Failed to convert close account refund recipient to pubkey"), + close_account_refund_recipient.key(), + )) + }); } + // Transfer the lamports from the fast market order to the close account refund recipient let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut(); - **refund_recipient.lamports.borrow_mut() += **fast_market_order_lamports; + **close_account_refund_recipient.lamports.borrow_mut() += **fast_market_order_lamports; **fast_market_order_lamports = 0; Ok(()) diff --git a/solana/programs/matching-engine/src/fallback/processor/errors.rs b/solana/programs/matching-engine/src/fallback/processor/errors.rs deleted file mode 100644 index 991c22f70..000000000 --- a/solana/programs/matching-engine/src/fallback/processor/errors.rs +++ /dev/null @@ -1,35 +0,0 @@ -use anchor_lang::prelude::*; - -// TODO: Move these into the matching engine error code enum -#[error_code] -pub enum FallbackError { - #[msg("Account is already initialized")] - AccountAlreadyInitialized, - - #[msg("From and to endpoints are the same")] - SameEndpoints, - - #[msg("Invalid PDA")] - InvalidPda, - - #[msg("Account data too small")] - AccountDataTooSmall, - - #[msg("Borsh Deserialization Error")] - BorshDeserializationError, - - #[msg("Invalid mint")] - InvalidMint, - - #[msg("Account not writable")] - AccountNotWritable, - - #[msg("Token transfer failed")] - TokenTransferFailed, - - #[msg("Invalid CCTP message")] - InvalidCctpMessage, - - #[msg("Invalid program")] - InvalidProgram, -} diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index ab10e22f1..cfe141c47 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,3 +1,4 @@ +use super::helpers::check_account_length; use crate::state::{ Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, @@ -12,7 +13,6 @@ use solana_program::instruction::Instruction; use solana_program::program::invoke_signed_unchecked; use super::burn_and_post::{burn_and_post, PostMessageAccounts}; -use super::errors::FallbackError; use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; @@ -153,7 +153,11 @@ impl ExecuteOrderCctpShim<'_> { } } } + pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { + // This saves stack space whereas having that in the body does not + check_account_length(accounts, 31)?; + let program_id = &crate::ID; // Get the accounts @@ -169,8 +173,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let initial_offer_token_account = &accounts[9]; let initial_participant_account = &accounts[10]; let to_router_endpoint_account = &accounts[11]; - // TODO: These are not used, so can I just ignore them? - // TODO: Check that this is the correct program id for the post message shim program let _post_message_shim_program_account = &accounts[12]; let _post_message_sequence_account = &accounts[13]; let _post_message_message_account = &accounts[14]; @@ -202,7 +204,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Check cctp message is mutable if !cctp_message_account.is_writable { msg!("Cctp message is not writable"); - return Err(FallbackError::AccountNotWritable.into()) + return Err(MatchingEngineError::AccountNotWritable.into()) .map_err(|e: Error| e.with_account_name("cctp_message")); } @@ -244,7 +246,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, fast_market_order_digest.as_ref(), - fast_market_order_zero_copy.refund_recipient.as_ref(), + fast_market_order_zero_copy + .close_account_refund_recipient + .as_ref(), ]; let (fast_market_order_pda, _fast_market_order_bump) = @@ -274,8 +278,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let mut active_auction = Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; - // TODO: Check that the auction has reached its deadline - // Correct way to use create_program_address with existing seeds and bump let active_auction_pda = Pubkey::create_program_address( &[ @@ -287,7 +289,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { ) .map_err(|_| { msg!("Failed to create program address with known bump"); - FallbackError::InvalidPda + MatchingEngineError::InvalidPda })?; if active_auction_pda != active_auction_account.key() { msg!("Active auction pda is invalid"); @@ -314,7 +316,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { ) .map_err(|_| { msg!("Failed to create program address with known bump"); - FallbackError::InvalidPda + MatchingEngineError::InvalidPda })?; if active_auction_custody_token_pda != active_auction_custody_token_account.key() { msg!("Active auction custody token pda is invalid"); @@ -335,6 +337,14 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .map_err(|e: Error| e.with_account_name("active_auction_config")); }; + // Check that the auction has reached its deadline + let auction_info = active_auction.info.as_ref().unwrap(); + if auction_info.within_auction_duration(&active_auction_config.parameters) { + msg!("Auction has not reached its deadline"); + return Err(MatchingEngineError::AuctionPeriodNotExpired.into()) + .map_err(|e: Error| e.with_account_name("active_auction")); + } + // Check active auction best offer token address if active_auction_best_offer_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token diff --git a/solana/programs/matching-engine/src/fallback/processor/create_account.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs similarity index 95% rename from solana/programs/matching-engine/src/fallback/processor/create_account.rs rename to solana/programs/matching-engine/src/fallback/processor/helpers.rs index cbdce83d9..f88f17e26 100644 --- a/solana/programs/matching-engine/src/fallback/processor/create_account.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,10 +1,20 @@ use anchor_lang::prelude::*; + use solana_program::{ entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, program::invoke_signed_unchecked, system_instruction, }; + +#[inline(always)] +pub fn check_account_length(accounts: &[AccountInfo], len: usize) -> Result<()> { + if accounts.len() < len { + return Err(ErrorCode::AccountNotEnoughKeys.into()); + } + Ok(()) +} + pub fn create_account_reliably( payer_key: &Pubkey, account_key: &Pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 89ba6b900..4d1c19788 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -4,18 +4,25 @@ use bytemuck::{Pod, Zeroable}; use solana_program::instruction::Instruction; use solana_program::program::invoke_signed_unchecked; -use super::create_account::create_account_reliably; +use super::helpers::create_account_reliably; -use super::errors::FallbackError; +use super::helpers::check_account_length; use super::FallbackMatchingEngineInstruction; +use crate::error::MatchingEngineError; use crate::state::FastMarketOrder as FastMarketOrderState; pub struct InitialiseFastMarketOrderAccounts<'ix> { + /// The signer of the transaction pub signer: &'ix Pubkey, + /// The fast market order account pubkey (that is created by the instruction) pub fast_market_order_account: &'ix Pubkey, + /// The guardian set account pubkey pub guardian_set: &'ix Pubkey, + /// The guardian set signatures account pubkey (created by the post verify vaa shim program) pub guardian_set_signatures: &'ix Pubkey, + /// The verify vaa shim program pubkey pub verify_vaa_shim_program: &'ix Pubkey, + /// The system program account pubkey pub system_program: &'ix Pubkey, } @@ -35,11 +42,15 @@ impl<'ix> InitialiseFastMarketOrderAccounts<'ix> { #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct InitialiseFastMarketOrderData { + /// The fast market order as the bytemuck struct pub fast_market_order: FastMarketOrderState, + /// The guardian set bump pub guardian_set_bump: u8, + /// Padding to ensure bytemuck deserialization works _padding: [u8; 7], } impl InitialiseFastMarketOrderData { + // Adds the padding to the InitialiseFastMarketOrderData pub fn new(fast_market_order: FastMarketOrderState, guardian_set_bump: u8) -> Self { Self { fast_market_order, @@ -48,6 +59,15 @@ impl InitialiseFastMarketOrderData { } } + /// Deserializes the InitialiseFastMarketOrderData from a byte slice + /// + /// # Arguments + /// + /// * `data` - A byte slice containing the InitialiseFastMarketOrderData + /// + /// # Returns + /// + /// Option<&Self> - The deserialized InitialiseFastMarketOrderData or None if the byte slice is not the correct length pub fn from_bytes(data: &[u8]) -> Option<&Self> { bytemuck::try_from_bytes::(data).ok() } @@ -69,13 +89,27 @@ impl InitialiseFastMarketOrder<'_> { } } +/// Initialises the fast market order account +/// +/// The verify shim program first checks that the digest of the fast market order is correct, and that the guardian signature is correct and recoverable. +/// If this is the case, the fast market order account is created. The fast market order account is owned by the matching engine program. It can be closed +/// by the close fast market order instruction, which returns the lamports to the close account refund recipient. +/// +/// # Arguments +/// +/// * `accounts` - The accounts of the fast market order and the guardian set +/// +/// # Returns +/// +/// Result<()> pub fn initialise_fast_market_order( accounts: &[AccountInfo], data: &InitialiseFastMarketOrderData, ) -> Result<()> { - if accounts.len() < 6 { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } + check_account_length(accounts, 6)?; + + let program_id = crate::ID; + let signer = &accounts[0]; let fast_market_order_account = &accounts[1]; let guardian_set = &accounts[2]; @@ -88,6 +122,7 @@ pub fn initialise_fast_market_order( guardian_set_bump, _padding: _, } = *data; + // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ let fast_market_order_digest = fast_market_order.digest(); @@ -121,32 +156,32 @@ pub fn initialise_fast_market_order( // ------------------------------------------------------------------------------------------------ // End of cpi call to verify the shim. + // Start of fast market order account creation + // ------------------------------------------------------------------------------------------------ let fast_market_order_key = fast_market_order_account.key(); - // Create the fast market order account - let program_id = crate::ID; let space = 8 + std::mem::size_of::(); let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrderState::SEED_PREFIX, fast_market_order_digest.as_ref(), - fast_market_order.refund_recipient.as_ref(), + fast_market_order.close_account_refund_recipient.as_ref(), ], &program_id, ); if fast_market_order_pda != fast_market_order_key { msg!("Fast market order pda is invalid"); - return Err(FallbackError::InvalidPda.into()) + return Err(MatchingEngineError::InvalidPda.into()) .map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); } let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, fast_market_order_digest.as_ref(), - fast_market_order.refund_recipient.as_ref(), + fast_market_order.close_account_refund_recipient.as_ref(), &[fast_market_order_bump], ]; let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; - // Create the account using the system program + // Create the account using the system program. The create account reliably ensures that the account creation cannot be raced. create_account_reliably( &signer.key(), &fast_market_order_key, @@ -168,12 +203,14 @@ pub fn initialise_fast_market_order( // Ensure the destination has enough space if fast_market_order_account_data.len() < 8 + fast_market_order_bytes.len() { msg!("Account data buffer too small"); - return Err(FallbackError::AccountDataTooSmall.into()); + return Err(MatchingEngineError::AccountDataTooSmall.into()); } // Write the fast_market_order struct to the account fast_market_order_account_data[8..8 + fast_market_order_bytes.len()] .copy_from_slice(fast_market_order_bytes); + // End of fast market order account creation + // ------------------------------------------------------------------------------------------------ Ok(()) } diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 1a1986f5c..259faa1e8 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -2,9 +2,9 @@ pub mod process_instruction; pub use process_instruction::*; pub mod burn_and_post; pub mod close_fast_market_order; -pub mod create_account; -pub mod errors; pub mod execute_order; pub mod initialise_fast_market_order; pub mod place_initial_offer; pub mod prepare_order_response; + +pub mod helpers; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 13dd731d2..2099b1587 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,4 +1,5 @@ -use super::create_account::create_account_reliably; +use super::helpers::check_account_length; +use super::helpers::create_account_reliably; use crate::state::{ Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, @@ -14,7 +15,6 @@ use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; use solana_program::program_pack::Pack; -use super::errors::FallbackError; use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; @@ -187,9 +187,7 @@ pub fn place_initial_offer_cctp_shim( let program_id = &crate::ID; // Your program ID // Check all accounts are valid - if accounts.len() < 11 { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } + check_account_length(accounts, 11)?; // Extract data fields // TODO: Remove sequence, vaa_time because they are in the fast market order state let PlaceInitialOfferCctpShimData { @@ -271,7 +269,7 @@ pub fn place_initial_offer_cctp_shim( // Check usdc mint if usdc.key() != common::USDC_MINT { msg!("Usdc mint is invalid"); - return Err(FallbackError::InvalidMint.into()); + return Err(MatchingEngineError::InvalidMint.into()); } // Check from_endpoint owner @@ -369,7 +367,7 @@ pub fn place_initial_offer_cctp_shim( auction_custody_token.key(), auction_custody_token_pda ); - return Err(FallbackError::InvalidPda.into()); + return Err(MatchingEngineError::InvalidPda.into()); } let auction_custody_token_seeds = [ @@ -411,7 +409,7 @@ pub fn place_initial_offer_cctp_shim( if pda != auction_key { msg!("Auction pda is invalid"); - return Err(FallbackError::InvalidPda.into()); + return Err(MatchingEngineError::InvalidPda.into()); } let auction_seeds = [Auction::SEED_PREFIX, vaa_message_digest.as_ref(), &[bump]]; let auction_signer_seeds = &[&auction_seeds[..]]; @@ -427,7 +425,7 @@ pub fn place_initial_offer_cctp_shim( // Borrow the account data mutably let mut data = auction_account .try_borrow_mut_data() - .map_err(|_| FallbackError::AccountNotWritable)?; + .map_err(|_| MatchingEngineError::AccountNotWritable)?; // Write the discriminator to the first 8 bytes let discriminator = Auction::discriminator(); @@ -470,7 +468,7 @@ pub fn place_initial_offer_cctp_shim( // Write the auction struct to the account let auction_bytes = auction_to_write .try_to_vec() - .map_err(|_| FallbackError::BorshDeserializationError)?; + .map_err(|_| MatchingEngineError::BorshDeserializationError)?; data[8..8 + auction_bytes.len()].copy_from_slice(&auction_bytes); // ------------------------------------------------------------------------------------------------ // End of initialisation of auction account @@ -500,7 +498,7 @@ pub fn place_initial_offer_cctp_shim( &[transfer_authority_bump], ]], ) - .map_err(|_| FallbackError::TokenTransferFailed)?; + .map_err(|_| MatchingEngineError::TokenTransferFailed)?; // ------------------------------------------------------------------------------------------------ // End of token transfer from offer token to auction custody token Ok(()) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index f19f847a9..c8c2a2cc7 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -1,7 +1,8 @@ use std::io::Cursor; -use super::create_account::create_account_reliably; +use super::helpers::create_account_reliably; use super::FallbackMatchingEngineInstruction; +use crate::fallback::helpers::check_account_length; use crate::state::PreparedOrderResponseInfo; use crate::state::PreparedOrderResponseSeeds; use crate::state::{ @@ -19,7 +20,6 @@ use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; use solana_program::program_pack::Pack; -use super::errors::FallbackError; use crate::error::MatchingEngineError; #[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] @@ -193,9 +193,7 @@ pub fn prepare_order_response_cctp_shim( data: PrepareOrderResponseCctpShimData, ) -> Result<()> { let program_id = &crate::ID; - if accounts.len() < 27 { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } + check_account_length(accounts, 27)?; let signer = &accounts[0]; let custodian = &accounts[1]; @@ -242,8 +240,6 @@ pub fn prepare_order_response_cctp_shim( let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; let finalised_vaa_consistency_level = fast_market_order_zero_copy.vaa_consistency_level; - msg!("Finalised vaa sequence: {:?}", finalised_vaa_sequence); - msg!("Finalised vaa nonce: {:?}", finalised_vaa_nonce); &finalized_vaa_message.digest( finalised_vaa_sequence, finalised_vaa_timestamp, @@ -254,21 +250,11 @@ pub fn prepare_order_response_cctp_shim( ) }; - msg!( - "Finalized VAA message digest: {:?}", - finalized_vaa_message_digest - ); - // Check that fast market order is owned by the program - if fast_market_order.owner != program_id { - msg!( - "Fast market order owner is invalid: expected {}, got {}", - program_id, - fast_market_order.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - } + require!( + fast_market_order.owner == program_id, + ErrorCode::ConstraintOwner + ); let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; @@ -309,111 +295,85 @@ pub fn prepare_order_response_cctp_shim( Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); // Check custodian account - if custodian.owner != program_id { - msg!( - "Custodian owner is invalid: expected {}, got {}", - program_id, - custodian.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - } + require!(custodian.owner == program_id, ErrorCode::ConstraintOwner); - if checked_custodian.paused { - msg!("Custodian is paused"); - return Err(ErrorCode::ConstraintRaw.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - } + require!(!checked_custodian.paused, MatchingEngineError::Paused); // Check usdc mint - if usdc.key() != common::USDC_MINT { - msg!("Usdc mint is invalid"); - return Err(FallbackError::InvalidMint.into()); - } + require!( + usdc.key() == common::USDC_MINT, + MatchingEngineError::InvalidMint + ); // Check from_endpoint owner - if from_endpoint.owner != program_id { - msg!( - "From endpoint owner is invalid: expected {}, got {}", - program_id, - from_endpoint.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("from_endpoint")); - } + require!( + from_endpoint.owner == program_id, + ErrorCode::ConstraintOwner + ); // Check to_endpoint owner - if to_endpoint.owner != program_id { - msg!( - "To endpoint owner is invalid: expected {}, got {}", - program_id, - to_endpoint.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("to_endpoint")); - } + require!(to_endpoint.owner == program_id, ErrorCode::ConstraintOwner); // Check that the from and to endpoints are different - if from_endpoint_account.chain == to_endpoint_account.chain { - return Err(MatchingEngineError::SameEndpoint.into()); - } + require!( + from_endpoint_account.chain != to_endpoint_account.chain, + MatchingEngineError::SameEndpoint + ); // Check that the to endpoint protocol is cctp or local - match to_endpoint_account.protocol { - MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), - _ => return Err(MatchingEngineError::InvalidEndpoint.into()), - } + require!( + matches!( + to_endpoint_account.protocol, + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } + ), + MatchingEngineError::InvalidEndpoint + ); // Check that the from endpoint protocol is cctp or local - match from_endpoint_account.protocol { - MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), - _ => return Err(MatchingEngineError::InvalidEndpoint.into()), - } + require!( + matches!( + from_endpoint_account.protocol, + MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } + ), + MatchingEngineError::InvalidEndpoint + ); // Check that to endpoint chain is equal to the fast_market_order target_chain - if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { - msg!("To endpoint chain is not equal to the fast_market_order target_chain. Expected {}, got {}", fast_market_order_zero_copy.target_chain, to_endpoint_account.chain); - return Err(MatchingEngineError::InvalidTargetRouter.into()); - } + require!( + to_endpoint_account.chain == fast_market_order_zero_copy.target_chain, + MatchingEngineError::InvalidTargetRouter + ); - if prepared_order_response_pda != prepared_order_response.key() { - msg!("Prepared order response pda is invalid"); - return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| { - e.with_pubkeys((prepared_order_response_pda, prepared_order_response.key())) - }); - } + require!( + prepared_order_response_pda == prepared_order_response.key(), + MatchingEngineError::InvalidPda + ); - if prepared_custody_token_pda != prepared_custody_token.key() { - msg!("Prepared custody token pda is invalid"); - return Err(FallbackError::InvalidPda.into()).map_err(|e: Error| { - e.with_pubkeys((prepared_custody_token_pda, prepared_custody_token.key())) - }); - } + require!( + prepared_custody_token_pda == prepared_custody_token.key(), + MatchingEngineError::InvalidPda + ); // Check the base token fee key is not equal to the prepared custody token key - if base_fee_token.key() == prepared_custody_token.key() { - msg!("Base token fee key is equal to the prepared custody token key"); - return Err(MatchingEngineError::InvalidBaseFeeToken.into()) - .map_err(|e: Error| e.with_account_name("base_fee_token")); - } + require!( + base_fee_token.key() != prepared_custody_token.key(), + MatchingEngineError::InvalidBaseFeeToken + ); - if token_program.key() != spl_token::ID { - msg!("Token program is invalid"); - return Err(FallbackError::InvalidProgram.into()) - .map_err(|e: Error| e.with_account_name("token_program")); - } + require!( + token_program.key() == spl_token::ID, + MatchingEngineError::InvalidProgram + ); - if _verify_shim_program.key() != wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID { - msg!("Verify shim program is invalid"); - return Err(FallbackError::InvalidProgram.into()) - .map_err(|e: Error| e.with_account_name("verify_shim_program")); - } + require!( + _verify_shim_program.key() == wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + MatchingEngineError::InvalidProgram + ); - if system_program.key() != solana_program::system_program::ID { - msg!("System program is invalid"); - return Err(FallbackError::InvalidProgram.into()) - .map_err(|e: Error| e.with_account_name("system_program")); - } + require!( + system_program.key() == solana_program::system_program::ID, + MatchingEngineError::InvalidProgram + ); // Verify deposit message shim using verify shim program @@ -493,11 +453,11 @@ pub fn prepare_order_response_cctp_shim( // Use cursor in order to write the prepared order response account data let prepared_order_response_data: &mut [u8] = &mut prepared_order_response .try_borrow_mut_data() - .map_err(|_| FallbackError::AccountNotWritable)?; + .map_err(|_| MatchingEngineError::AccountNotWritable)?; let mut cursor = Cursor::new(prepared_order_response_data); prepared_order_response_account_to_write .try_serialize(&mut cursor) - .map_err(|_| FallbackError::BorshDeserializationError)?; + .map_err(|_| MatchingEngineError::BorshDeserializationError)?; // End create prepared order response account // ------------------------------------------------------------------------------------------------ @@ -574,7 +534,7 @@ pub fn prepare_order_response_cctp_shim( .unwrap(); invoke_signed_unchecked(&transfer_ix, accounts, &[Custodian::SIGNER_SEEDS]) - .map_err(|_| FallbackError::TokenTransferFailed)?; + .map_err(|_| MatchingEngineError::TokenTransferFailed)?; Ok(()) } diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 036159b08..ccafb3f8a 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -12,23 +12,23 @@ use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; impl<'ix> FallbackMatchingEngineInstruction<'ix> { - pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); - pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:execute_order_cctp_shim"); pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:initialise_fast_market_order"); pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:close_fast_market_order"); + pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); + pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:execute_order_cctp_shim"); pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); } pub enum FallbackMatchingEngineInstruction<'ix> { - PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), - ExecuteOrderCctpShim, InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), CloseFastMarketOrder, + PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), + ExecuteOrderCctpShim, PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } @@ -43,18 +43,18 @@ pub fn process_instruction( let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data).unwrap(); match instruction { - FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { - place_initial_offer_cctp_shim(accounts, &data) - } - FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { - handle_execute_order_shim(accounts) - } FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(data) => { initialise_fast_market_order(accounts, &data) } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { close_fast_market_order(accounts) } + FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { + place_initial_offer_cctp_shim(accounts, &data) + } + FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { + handle_execute_order_shim(accounts) + } FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { prepare_order_response_cctp_shim(accounts, data) } diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index cc694ec39..1a4a59738 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -1,28 +1,49 @@ use anchor_lang::prelude::*; use solana_program::keccak; +/// An account that represents a fast market order vaa. It is created by the signer of the transaction, and owned by the matching engine program. +/// The of the account is able to close this account and redeem the lamports deposited into the account (for rent) #[account(zero_copy)] #[derive(Debug)] #[repr(C)] pub struct FastMarketOrder { + /// The amount of tokens sent from the source chain via the fast transfer pub amount_in: u64, + /// The minimum amount of tokens to be received on the target chain via the fast transfer pub min_amount_out: u64, + /// The deadline of the auction pub deadline: u32, + /// The target chain (represented as a wormhole chain id) pub target_chain: u16, + /// The length of the redeemer message pub redeemer_message_length: u16, + /// The redeemer of the fast transfer (on the destination chain) pub redeemer: [u8; 32], + /// The sender of the fast transfer (on the source chain) pub sender: [u8; 32], + /// The refund address of the fast transfer pub refund_address: [u8; 32], + /// The maximum fee of the fast transfer pub max_fee: u64, + /// The initial auction fee of the fast transfer pub init_auction_fee: u64, + /// The redeemer message of the fast transfer pub redeemer_message: [u8; 512], - pub refund_recipient: [u8; 32], + /// The refund recipient for the creator of the fast market order account + pub close_account_refund_recipient: [u8; 32], + /// The emitter address of the fast transfer pub vaa_emitter_address: [u8; 32], + /// The sequence of the fast transfer vaa pub vaa_sequence: u64, + /// The timestamp of the fast transfer vaa pub vaa_timestamp: u32, + /// The vaa nonce, which is not used and can be set to 0 pub vaa_nonce: u32, + /// The source chain of the fast transfer vaa (represented as a wormhole chain id) pub vaa_emitter_chain: u16, + /// The consistency level of the fast transfer vaa pub vaa_consistency_level: u8, + /// Not used, but required for bytemuck serialisation _padding: [u8; 5], } @@ -39,7 +60,7 @@ impl FastMarketOrder { max_fee: u64, init_auction_fee: u64, redeemer_message: [u8; 512], - refund_recipient: [u8; 32], + close_account_refund_recipient: [u8; 32], vaa_sequence: u64, vaa_timestamp: u32, vaa_nonce: u32, @@ -59,7 +80,7 @@ impl FastMarketOrder { max_fee, init_auction_fee, redeemer_message, - refund_recipient, + close_account_refund_recipient, vaa_sequence, vaa_timestamp, vaa_nonce, @@ -72,6 +93,7 @@ impl FastMarketOrder { pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; + /// Convert the fast market order to a vec of bytes (without the discriminator) pub fn to_vec(&self) -> Vec { let payload_slice = bytemuck::bytes_of(self); let mut payload = Vec::with_capacity(payload_slice.len()); @@ -79,6 +101,7 @@ impl FastMarketOrder { payload } + /// Creates an payload as expected in a fast market order vaa pub fn payload(&self) -> Vec { let mut payload = vec![]; payload.push(11_u8); @@ -99,6 +122,7 @@ impl FastMarketOrder { payload } + /// A double hash of the serialised fast market order. Used for seeds and verification. pub fn digest(&self) -> [u8; 32] { let message_hash = keccak::hashv(&[ self.vaa_timestamp.to_be_bytes().as_ref(), diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 30194ced0..9e47aec14 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -1,5 +1,6 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; +use matching_engine::error::MatchingEngineError; use matching_engine::state::FastMarketOrder; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use shimless::execute_order::execute_order_shimless_test; @@ -7,20 +8,23 @@ use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; mod shimful; mod shimless; +mod testing_engine; mod utils; +use crate::testing_engine::config::{ + ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, + PlaceInitialOfferInstructionConfig, +}; +use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; use shimful::shims::{ initialise_fast_market_order_fallback_instruction, place_initial_offer_fallback, place_initial_offer_fallback_test, set_up_post_message_transaction_test, }; use shimful::shims_execute_order::execute_order_fallback_test; -use shimless::initialize::initialize_program; +use shimless::initialize::{initialize_program, AuctionParametersConfig}; use shimless::make_offer::{improve_offer, place_initial_offer_shimless}; use solana_sdk::transaction::VersionedTransaction; use utils::auction::AuctionAccounts; -use utils::router::{ - add_local_router_endpoint_ix, create_all_router_endpoints_test, - create_cctp_router_endpoints_test, -}; +use utils::router::{add_local_router_endpoint_ix, create_all_router_endpoints_test}; use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; use utils::vaa::VaaArgs; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; @@ -31,23 +35,19 @@ pub async fn test_initialize_program() { let testing_context = setup_environment( ShimMode::None, TransferDirection::FromArbitrumToEthereum, - None, + None, // Vaa args for creating vaas ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_config = InitializeInstructionConfig::default(); - // Check that custodian data corresponds to the expected values - initialize_fixture.verify_custodian( - testing_context.testing_actors.owner.pubkey(), - testing_context.testing_actors.owner_assistant.pubkey(), - testing_context - .testing_actors - .fee_recipient - .token_account - .unwrap() - .address, - ); + let testing_engine = TestingEngine::new( + testing_context, + vec![InstructionTrigger::InitializeProgram(initialize_config)], + ) + .await; + + testing_engine.execute().await; } /// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains @@ -60,17 +60,18 @@ pub async fn test_cctp_token_router_endpoint_creation() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_config = InitializeInstructionConfig::default(); - let token_router_endpoints = create_cctp_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), + let testing_engine = TestingEngine::new( + testing_context, + vec![ + InstructionTrigger::InitializeProgram(initialize_config), + InstructionTrigger::CreateCctpRouterEndpoints, + ], ) .await; - assert_eq!(token_router_endpoints.len(), 2); + testing_engine.execute().await; } #[tokio::test] @@ -82,7 +83,10 @@ pub async fn test_local_token_router_endpoint_creation() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let _local_token_router_endpoint = add_local_router_endpoint_ix( &testing_context, @@ -103,67 +107,24 @@ pub async fn test_setup_vaas() { post_vaa: true, ..VaaArgs::default() }; - let mut testing_context = + let testing_context = setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; testing_context.verify_vaas().await; - let initialize_fixture = initialize_program(&testing_context).await; - - // Try making initial offer - let fast_vaa = testing_context - .get_vaa_pair(0) - .expect("Failed to get vaa pair") - .fast_transfer_vaa; - let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); - let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_endpoints = create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), - ) - .await; - - let solver = testing_context.testing_actors.solvers[0].clone(); - let auction_accounts = AuctionAccounts::new( - Some(fast_vaa_pubkey), // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - testing_context.get_usdc_mint_address(), // USDC mint pubkey - transfer_direction, - ); - - place_initial_offer_shimless( - &mut testing_context, - &auction_accounts, - fast_vaa, - PROGRAM_ID, - true, // Expected to pass - ) - .await; - let auction_state = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap(); - auction_state - .verify_initial_offer(&testing_context.test_context) - .await; - improve_offer( - &mut testing_context, - PROGRAM_ID, - solver, - auction_config_address, + let testing_engine = TestingEngine::new( + testing_context, + vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints, + InstructionTrigger::PlaceInitialOfferShimless( + PlaceInitialOfferInstructionConfig::default(), + ), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), + ], ) .await; - // TODO: Implement check on improved offer auction state - // auction_state - // .borrow() - // .verify_improved_offer(&testing_context.test_context) - // .await; + testing_engine.execute().await; } #[tokio::test] @@ -304,7 +265,7 @@ pub async fn test_close_fast_market_order_fallback() { &[ FastMarketOrder::SEED_PREFIX, &fast_market_order.digest(), - &fast_market_order.refund_recipient, + &fast_market_order.close_account_refund_recipient, ], &PROGRAM_ID, ) @@ -408,8 +369,10 @@ pub async fn test_place_initial_offer_fallback() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; - let auction_config_address = initialize_fixture.get_auction_config_address(); + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( &mut testing_context, &initialize_fixture, @@ -423,6 +386,8 @@ pub async fn test_place_initial_offer_fallback() { true, // Expected to pass ) .await; + let auction_config_address = initialize_fixture.get_auction_config_address(); + // Attempt to improve the offer using the non-fallback method with another solver making the improved offer println!("Improving offer"); let second_solver = testing_context.testing_actors.solvers[1].clone(); @@ -431,6 +396,8 @@ pub async fn test_place_initial_offer_fallback() { PROGRAM_ID, second_solver, auction_config_address, + 500_000, + None, ) .await; println!("Offer improved"); @@ -451,7 +418,10 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( &mut testing_context, &initialize_fixture, @@ -475,7 +445,10 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { &auction_accounts, first_test_ft, PROGRAM_ID, - false, // Expected to fail + Some(&ExpectedError { + instruction_index: 0, + error: MatchingEngineError::AccountAlreadyInitialized, + }), // Expected to fail ) .await; } @@ -494,7 +467,10 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( &mut testing_context, @@ -509,7 +485,7 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { &auction_accounts, first_test_ft, PROGRAM_ID, - true, // Expected to pass + None, // Expected to pass ) .await; // Now test with the fallback program (shims) and expect it to fail @@ -538,7 +514,10 @@ pub async fn test_execute_order_fallback() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( &mut testing_context, &initialize_fixture, @@ -592,7 +571,10 @@ pub async fn test_execute_order_shimless() { Some(vaa_args), ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; let first_test_fast_transfer_pubkey = first_test_fast_transfer.get_vaa_pubkey(); @@ -608,7 +590,7 @@ pub async fn test_execute_order_shimless() { &auction_accounts, first_test_fast_transfer, PROGRAM_ID, - true, // Expected to pass + None, // Expected to pass ) .await; @@ -629,7 +611,10 @@ pub async fn test_execute_order_fallback_blocks_shimless() { ) .await; let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( &mut testing_context, &initialize_fixture, @@ -677,7 +662,10 @@ pub async fn test_prepare_order_shim_fallback() { Some(vaa_args), ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); let payload_deserialized: utils::vaa::PayloadDeserialized = first_vaa_pair @@ -761,7 +749,10 @@ pub async fn test_settle_auction_complete() { ) .await; - let initialize_fixture = initialize_program(&testing_context).await; + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); diff --git a/solana/programs/matching-engine/tests/shimful/shims.rs b/solana/programs/matching-engine/tests/shimful/shims.rs index d3345286b..0669b2513 100644 --- a/solana/programs/matching-engine/tests/shimful/shims.rs +++ b/solana/programs/matching-engine/tests/shimful/shims.rs @@ -504,7 +504,7 @@ pub async fn place_initial_offer_fallback( &[ FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest(), - &fast_market_order.refund_recipient, + &fast_market_order.close_account_refund_recipient, ], program_id, ) @@ -567,6 +567,7 @@ pub async fn place_initial_offer_fallback( let new_active_auction_state = utils::auction::ActiveAuctionState { auction_address, auction_custody_token_address, + auction_config_address: auction_accounts.auction_config, initial_offer: utils::auction::AuctionOffer { offer_token: auction_accounts.offer_token, offer_price, @@ -603,7 +604,7 @@ pub fn initialise_fast_market_order_fallback_instruction( &[ FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest(), - &fast_market_order.refund_recipient, + &fast_market_order.close_account_refund_recipient, ], program_id, ) @@ -641,7 +642,7 @@ pub async fn close_fast_market_order_fallback( program_id: program_id, accounts: CloseFastMarketOrderFallbackAccounts { fast_market_order: fast_market_order_address, - refund_recipient: &refund_recipient_keypair.pubkey(), + close_account_refund_recipient: &refund_recipient_keypair.pubkey(), }, } .instruction(); diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index 132c56cc0..e942a02c8 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -185,7 +185,7 @@ pub async fn execute_order_fallback( // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; - utils::setup::fast_forward_slots(test_ctx, 1).await; + utils::setup::fast_forward_slots(test_ctx, 3).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), diff --git a/solana/programs/matching-engine/tests/shimless/initialize.rs b/solana/programs/matching-engine/tests/shimless/initialize.rs index 91eacd5a9..9cea6ca10 100644 --- a/solana/programs/matching-engine/tests/shimless/initialize.rs +++ b/solana/programs/matching-engine/tests/shimless/initialize.rs @@ -1,17 +1,16 @@ -use solana_program_test::ProgramTestContext; use solana_sdk::{ instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::{Transaction, VersionedTransaction}, }; -use std::cell::RefCell; -use std::rc::Rc; use anchor_lang::AccountDeserialize; use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; use solana_program::{bpf_loader_upgradeable, system_program}; +use crate::testing_engine::config::ExpectedError; + use super::super::TestingContext; use anchor_lang::{InstructionData, ToAccountMetas}; use matching_engine::{ @@ -27,7 +26,6 @@ pub struct InitializeAddresses { } pub struct InitializeFixture { - pub test_context: Rc>, pub custodian: Custodian, pub addresses: InitializeAddresses, } @@ -92,30 +90,75 @@ impl InitializeFixture { } } -pub async fn initialize_program(testing_context: &TestingContext) -> InitializeFixture { - let test_context = testing_context.test_context.clone(); +#[derive(Clone)] +pub struct AuctionParametersConfig { + // Auction config iid used for seeding the auction config account + pub config_id: u32, + // Fields in the auction parameters account + pub user_penalty_reward_bps: u32, + pub initial_penalty_bps: u32, + pub duration: u16, + pub grace_period: u16, + pub penalty_period: u16, + pub min_offer_delta_bps: u32, + pub security_deposit_base: u64, + pub security_deposit_bps: u32, +} + +impl Default for AuctionParametersConfig { + fn default() -> Self { + Self { + config_id: 0, + user_penalty_reward_bps: 250_000, // 25% + initial_penalty_bps: 250_000, // 25% + duration: 2, + grace_period: 5, + penalty_period: 10, + min_offer_delta_bps: 20_000, // 2% + security_deposit_base: 4_200_000, + security_deposit_bps: 5_000, // 0.5% + } + } +} + +impl Into for AuctionParametersConfig { + fn into(self) -> AuctionParameters { + AuctionParameters { + user_penalty_reward_bps: self.user_penalty_reward_bps, + initial_penalty_bps: self.initial_penalty_bps, + duration: self.duration, + grace_period: self.grace_period, + penalty_period: self.penalty_period, + min_offer_delta_bps: self.min_offer_delta_bps, + security_deposit_base: self.security_deposit_base, + security_deposit_bps: self.security_deposit_bps, + } + } +} + +pub async fn initialize_program( + testing_context: &TestingContext, + auction_parameters_config: AuctionParametersConfig, + expected_error: Option<&ExpectedError>, +) -> Option { + let test_context = &testing_context.test_context; let program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); let (custodian, _custodian_bump) = Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); + // TODO: Figure out this seed? Where does the 0u32 come from? let (auction_config, _auction_config_bump) = Pubkey::find_program_address( - &[AuctionConfig::SEED_PREFIX, &0u32.to_be_bytes()], + &[ + AuctionConfig::SEED_PREFIX, + &auction_parameters_config.config_id.to_be_bytes(), + ], &program_id, ); // Create AuctionParameters - let auction_params = AuctionParameters { - user_penalty_reward_bps: 250_000, // 25% - initial_penalty_bps: 250_000, // 25% - duration: 2, - grace_period: 5, - penalty_period: 10, - min_offer_delta_bps: 20_000, // 2% - security_deposit_base: 4_200_000, - security_deposit_bps: 5_000, // 0.5% - }; + let auction_params: AuctionParameters = auction_parameters_config.into(); // Create the instruction data let ix_data = matching_engine::instruction::Initialize { @@ -172,32 +215,33 @@ pub async fn initialize_program(testing_context: &TestingContext) -> InitializeF // Process transaction let versioned_transaction = VersionedTransaction::try_from(transaction) .expect("Failed to convert transaction to versioned transaction"); - test_context - .borrow_mut() - .banks_client - .process_transaction(versioned_transaction) - .await - .unwrap(); - // Verify the results - let custodian_account = test_context - .borrow_mut() - .banks_client - .get_account(custodian.clone()) - .await - .unwrap() - .unwrap(); - - let custodian_data = - Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); - let initialize_addresses = InitializeAddresses { - custodian_address: custodian, - auction_config_address: auction_config, - cctp_mint_recipient: cctp_mint_recipient, - }; - InitializeFixture { - test_context, - custodian: custodian_data, - addresses: initialize_addresses, + testing_context + .execute_and_verify_transaction(versioned_transaction, expected_error) + .await; + + if expected_error.is_none() { + // Verify the results + let custodian_account = test_context + .borrow_mut() + .banks_client + .get_account(custodian.clone()) + .await + .expect("Failed to get custodian account") + .expect("Custodian account not found"); + + let custodian_data = + Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + let initialize_addresses = InitializeAddresses { + custodian_address: custodian, + auction_config_address: auction_config, + cctp_mint_recipient: cctp_mint_recipient, + }; + Some(InitializeFixture { + custodian: custodian_data, + addresses: initialize_addresses, + }) + } else { + None } } diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs index b16b190bb..dc4544bd8 100644 --- a/solana/programs/matching-engine/tests/shimless/make_offer.rs +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -1,3 +1,5 @@ +use crate::testing_engine::config::ExpectedError; + use super::super::utils; use anchor_lang::prelude::*; use anchor_lang::InstructionData; @@ -24,7 +26,7 @@ pub async fn place_initial_offer_shimless( accounts: &AuctionAccounts, fast_market_order: TestVaa, program_id: Pubkey, - expected_to_pass: bool, + expected_error: Option<&ExpectedError>, ) { let test_ctx = &testing_context.test_context; let owner_keypair = testing_context.testing_actors.owner.keypair(); @@ -140,16 +142,16 @@ pub async fn place_initial_offer_shimless( test_ctx.borrow().last_blockhash, ); - let tx_result = test_ctx - .borrow_mut() - .banks_client - .process_transaction(tx) + testing_context + .execute_and_verify_transaction(tx, expected_error) .await; - assert_eq!(tx_result.is_ok(), expected_to_pass); - if tx_result.is_ok() { + + // If the transaction failed and we expected it to pass, we would not get here + if expected_error.is_none() { testing_context.testing_state.auction_state = AuctionState::Active(ActiveAuctionState { auction_address, auction_custody_token_address, + auction_config_address: accounts.auction_config, initial_offer: AuctionOffer { offer_token: accounts.offer_token, offer_price: initial_offer_ix.offer_price, @@ -167,21 +169,20 @@ pub async fn improve_offer( program_id: Pubkey, solver: Solver, auction_config: Pubkey, + offer_price: u64, + expected_error: Option<&ExpectedError>, ) { let test_ctx = &testing_context.test_context; let owner_keypair = testing_context.testing_actors.owner.keypair(); - let auction_state = &mut testing_context + let auction_state = testing_context .testing_state .auction_state - .get_active_auction_mut() + .get_active_auction() .unwrap(); let auction_address = auction_state.auction_address; let auction_custody_token_address = auction_state.auction_custody_token_address; - // Decrease the offer by 0.5 usdc - let improve_offer_ix = ImproveOfferIx { - offer_price: auction_state.best_offer.offer_price - 500_000, - }; + let improve_offer_ix = ImproveOfferIx { offer_price }; let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; let transfer_authority = Pubkey::find_program_address( @@ -234,15 +235,20 @@ pub async fn improve_offer( test_ctx.borrow().last_blockhash, ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(tx) - .await - .expect("Failed to improve offer"); + testing_context + .execute_and_verify_transaction(tx, expected_error) + .await; - auction_state.best_offer = AuctionOffer { - offer_token, - offer_price: improve_offer_ix.offer_price, - }; + // If the transaction failed and we expected it to pass, we would not get here + if expected_error.is_none() { + let auction_state = testing_context + .testing_state + .auction_state + .get_active_auction_mut() + .unwrap(); + auction_state.best_offer = AuctionOffer { + offer_token, + offer_price: improve_offer_ix.offer_price, + }; + } } diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs new file mode 100644 index 000000000..e6a5600fc --- /dev/null +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -0,0 +1,82 @@ +use crate::shimless::initialize::AuctionParametersConfig; +use matching_engine::error::MatchingEngineError; + +#[derive(Clone)] +pub struct ExpectedError { + pub instruction_index: u8, + pub error: MatchingEngineError, +} + +#[derive(Clone)] +pub struct InitializeInstructionConfig { + pub auction_parameters_config: AuctionParametersConfig, + pub expected_error: Option, +} + +impl Default for InitializeInstructionConfig { + fn default() -> Self { + Self { + auction_parameters_config: AuctionParametersConfig::default(), + expected_error: None, + } + } +} + +#[derive(Clone)] +pub struct InitializeFastMarketOrderShimInstructionConfig { + pub fast_market_order_config: FastMarketOrderConfig, + pub expected_error: Option, +} + +impl Default for InitializeFastMarketOrderShimInstructionConfig { + fn default() -> Self { + Self { + fast_market_order_config: FastMarketOrderConfig::default(), + expected_error: None, + } + } +} + +pub struct PlaceInitialOfferInstructionConfig { + pub solver_index: usize, + pub offer_price: u64, + pub expected_error: Option, +} + +impl Default for PlaceInitialOfferInstructionConfig { + fn default() -> Self { + Self { + solver_index: 0, + offer_price: 1__000_000, + expected_error: None, + } + } +} + +pub struct ImproveOfferInstructionConfig { + pub solver_index: usize, + pub offer_price: u64, + pub expected_error: Option, +} + +impl Default for ImproveOfferInstructionConfig { + fn default() -> Self { + Self { + solver_index: 0, + offer_price: 500_000, + expected_error: None, + } + } +} +#[derive(Clone)] +pub struct FastMarketOrderConfig { + pub fast_market_order_id: u32, +} + +impl Default for FastMarketOrderConfig { + fn default() -> Self { + Self { + fast_market_order_id: 0, + } + } +} diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs new file mode 100644 index 000000000..ea31a2589 --- /dev/null +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -0,0 +1,236 @@ +use std::{cell::RefCell, rc::Rc}; + +use super::config::*; +use crate::shimless::{ + initialize::{initialize_program, InitializeFixture}, + make_offer::{improve_offer, place_initial_offer_shimless}, +}; +use crate::utils::{ + auction::AuctionAccounts, + router::{create_all_router_endpoints_test, TestRouterEndpoints}, + setup::TestingContext, +}; + +#[allow(dead_code)] +pub enum InstructionTrigger { + InitializeProgram(InitializeInstructionConfig), + CreateCctpRouterEndpoints, + InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), + CloseFastMarketOrderShim, + PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), + PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), + ImproveOfferShimless(ImproveOfferInstructionConfig), + ExecuteOrderShimless, + ExecuteOrderShim, + PrepareOrderShimless, + PrepareOrderShim, + SettleAuction, +} + +pub struct InstructionTriggerResults { + pub initialize_program: Option, + pub create_cctp_router_endpoints: Option, +} + +impl InstructionTriggerResults { + pub fn new() -> Self { + Self { + initialize_program: None, + create_cctp_router_endpoints: None, + } + } +} + +pub struct TestingEngine { + pub testing_context: Rc>, + pub instruction_triggers: Vec, + pub instruction_trigger_results: Rc>, +} + +impl TestingEngine { + pub async fn new( + testing_context: TestingContext, + instruction_triggers: Vec, + ) -> Self { + Self { + testing_context: Rc::new(RefCell::new(testing_context)), + instruction_triggers, + instruction_trigger_results: Rc::new(RefCell::new(InstructionTriggerResults::new())), + } + } + + pub async fn execute(&self) { + for trigger in self.instruction_triggers.iter() { + self.execute_trigger(trigger).await; + } + } + + async fn execute_trigger(&self, trigger: &InstructionTrigger) { + match trigger { + InstructionTrigger::InitializeProgram(config) => { + self.instruction_trigger_results + .borrow_mut() + .initialize_program = self.initialize_program(config).await; + } + InstructionTrigger::CreateCctpRouterEndpoints => { + self.instruction_trigger_results + .borrow_mut() + .create_cctp_router_endpoints = self.create_cctp_router_endpoints().await; + } + InstructionTrigger::PlaceInitialOfferShimless(config) => { + self.place_initial_offer_shimless(config).await; + } + InstructionTrigger::ImproveOfferShimless(config) => { + self.improve_offer_shimless(config).await; + } + _ => panic!("Not implemented yet"), // Not implemented yet + } + } + + async fn initialize_program( + &self, + config: &InitializeInstructionConfig, + ) -> Option { + let auction_parameters_config = config.auction_parameters_config.clone(); + let expected_error = config.expected_error.as_ref(); + + let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { + let testing_context_ref = self.testing_context.borrow(); + let result = initialize_program( + &testing_context_ref, + auction_parameters_config, + expected_error, + ) + .await; + + let testing_actors = &testing_context_ref.testing_actors; + ( + result, + testing_actors.owner.pubkey(), + testing_actors.owner_assistant.pubkey(), + testing_actors + .fee_recipient + .token_account_address() + .unwrap(), + ) + }; + + if expected_error.is_none() { + let initialize_fixture = result.expect("Failed to initialize program"); + initialize_fixture.verify_custodian( + owner_pubkey, + owner_assistant_pubkey, + fee_recipient_token_account, + ); + + let auction_config_address = initialize_fixture.get_auction_config_address(); + self.testing_context + .borrow_mut() + .testing_state + .program_state + .initialize(auction_config_address); + return Some(initialize_fixture); + } + None + } + + async fn create_cctp_router_endpoints(&self) -> Option { + let custodian_address = self + .testing_context + .borrow() + .testing_state + .program_state + .get_custodian_address(); + let testing_actors = &self.testing_context.borrow().testing_actors; + let result = create_all_router_endpoints_test( + &self.testing_context.borrow(), + testing_actors.owner.pubkey(), + custodian_address, + testing_actors.owner.keypair(), + ) + .await; + Some(result) + } + + async fn place_initial_offer_shimless(&self, config: &PlaceInitialOfferInstructionConfig) { + let solver = + self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + let expected_error = config.expected_error.as_ref(); + let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); + let fast_vaa = testing_context + .get_vaa_pair(0) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); + let custodian_address = testing_context + .testing_state + .program_state + .get_custodian_address(); + let auction_config_address = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .auction_config_address; + + let auction_accounts = AuctionAccounts::new( + Some(fast_vaa_pubkey), + solver, + auction_config_address, + self.instruction_trigger_results + .borrow() + .create_cctp_router_endpoints + .as_ref() + .unwrap(), + custodian_address, + testing_context.get_usdc_mint_address(), + testing_context.testing_state.transfer_direction, + ); + place_initial_offer_shimless( + testing_context, + &auction_accounts, + fast_vaa, + testing_context.get_matching_engine_program_id(), + expected_error, + ) + .await; + if expected_error.is_none() { + self.testing_context + .borrow() + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .verify_initial_offer(&self.testing_context.borrow().test_context) + .await; + } + } + + async fn improve_offer_shimless(&self, config: &ImproveOfferInstructionConfig) { + let expected_error = config.expected_error.as_ref(); + let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); + let solver = + self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + let offer_price = config.offer_price; + let auction_config_address = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .auction_config_address; + improve_offer( + testing_context, + testing_context.get_matching_engine_program_id(), + solver, + auction_config_address, + offer_price, + expected_error, + ) + .await; + // TODO: Implement check on improved offer auction state + // auction_state + // .borrow() + // .verify_improved_offer(&testing_context.test_context) + // .await; + } +} diff --git a/solana/programs/matching-engine/tests/testing_engine/mod.rs b/solana/programs/matching-engine/tests/testing_engine/mod.rs new file mode 100644 index 000000000..abfec3e7d --- /dev/null +++ b/solana/programs/matching-engine/tests/testing_engine/mod.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod engine; +pub mod state; diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/programs/matching-engine/tests/testing_engine/state.rs new file mode 100644 index 000000000..e69de29bb diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index dbe965124..0ba5f2feb 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -44,6 +44,7 @@ impl AuctionState { pub struct ActiveAuctionState { pub auction_address: Pubkey, pub auction_custody_token_address: Pubkey, + pub auction_config_address: Pubkey, pub initial_offer: AuctionOffer, pub best_offer: AuctionOffer, } @@ -117,8 +118,8 @@ impl AuctionAccounts { impl ActiveAuctionState { // TODO: Figure this out - pub async fn verify_initial_offer(&self, testing_context: &Rc>) { - let auction_account = testing_context + pub async fn verify_initial_offer(&self, test_ctx: &Rc>) { + let auction_account = test_ctx .borrow_mut() .banks_client .get_account(self.auction_address) diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index bfe48fdb6..dfc6bb4f3 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -7,6 +7,8 @@ pub mod mint; pub mod program_fixtures; pub mod router; pub mod setup; +// pub mod testing_engine; +// pub mod testing_engine_configs; pub mod token_account; pub mod vaa; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index e47c18a84..1a8777513 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -13,13 +13,16 @@ use super::{ token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, }; use super::{Chain, REGISTERED_TOKEN_ROUTERS}; +use crate::testing_engine::config::ExpectedError; use anchor_lang::AccountDeserialize; use anchor_spl::token::{ spl_token::{self, instruction::approve}, TokenAccount, }; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; -use solana_program_test::{ProgramTest, ProgramTestContext}; +use solana_program_test::{BanksClientError, ProgramTest, ProgramTestContext}; +use solana_sdk::instruction::InstructionError; +use solana_sdk::transaction::{TransactionError, VersionedTransaction}; use solana_sdk::{ pubkey::Pubkey, signature::{Keypair, Signer}, @@ -114,23 +117,8 @@ impl PreTestingContext { } } -pub struct TestingState { - pub auction_state: AuctionState, - pub vaas: TestVaaPairs, - pub transfer_direction: TransferDirection, -} - -impl Default for TestingState { - fn default() -> Self { - Self { - auction_state: AuctionState::Inactive, - vaas: TestVaaPairs::new(), - transfer_direction: TransferDirection::FromEthereumToArbitrum, - } - } -} pub struct TestingContext { - pub program_data_account: Pubkey, // Move this into something smarter + pub program_data_account: Pubkey, // TODO: Move this into something smarter pub testing_actors: TestingActors, pub test_context: Rc>, pub fixture_accounts: Option, @@ -215,6 +203,58 @@ impl TestingContext { pub fn get_wormhole_program_id(&self) -> Pubkey { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } + + // TODO: Edit to handle multiple instructions in a single transaction + pub async fn execute_and_verify_transaction( + &self, + transaction: impl Into, + expected_error: Option<&ExpectedError>, + ) { + let tx_result = self + .test_context + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await; + if let Some(expected_error) = expected_error { + let tx_error = tx_result.expect_err(&format!( + "Expected error {:?}, but transaction succeeded", + expected_error.error + )); + + match tx_error { + BanksClientError::TransactionError(TransactionError::InstructionError( + instruction_index, + InstructionError::Custom(error_code), + )) => { + assert_eq!( + instruction_index, expected_error.instruction_index, + "Expected error on instruction {}, but got: {:?}", + expected_error.instruction_index, tx_error + ); + let expected_error_code = u32::from(expected_error.error); + + assert_eq!( + error_code, expected_error_code, + "Program returned error code {}, expected {} ({:?})", + error_code, expected_error_code, expected_error.error + ); + } + _ => { + panic!( + "Expected program error {:?}, but got: {:?}", + expected_error.error, tx_error + ); + } + } + } else { + assert!( + tx_result.is_ok(), + "Transaction failed but no error was expected: {:?}", + tx_result.err().unwrap() + ); + } + } } #[derive(Clone)] @@ -464,6 +504,48 @@ pub async fn fast_forward_slots(test_context: &Rc>, println!("Fast forwarded {} slots", num_slots); } +#[derive(Clone)] +pub enum ProgramState { + Initialized(ProgramAddresses), + Uninitialized, +} + +impl ProgramState { + pub fn initialize(&mut self, custodian_address: Pubkey) { + *self = ProgramState::Initialized(ProgramAddresses { custodian_address }); + } + + pub fn get_custodian_address(&self) -> Pubkey { + match self { + ProgramState::Initialized(addresses) => addresses.custodian_address, + ProgramState::Uninitialized => panic!("Program is not initialized"), + } + } +} + +#[derive(Clone)] +pub struct ProgramAddresses { + pub custodian_address: Pubkey, +} + +pub struct TestingState { + pub program_state: ProgramState, + pub auction_state: AuctionState, + pub vaas: TestVaaPairs, + pub transfer_direction: TransferDirection, +} + +impl Default for TestingState { + fn default() -> Self { + Self { + program_state: ProgramState::Uninitialized, + auction_state: AuctionState::Inactive, + vaas: TestVaaPairs::new(), + transfer_direction: TransferDirection::FromEthereumToArbitrum, + } + } +} + pub enum ShimMode { None, PostVaa, @@ -477,6 +559,27 @@ pub enum TransferDirection { FromEthereumToArbitrum, } +impl Default for TransferDirection { + fn default() -> Self { + Self::FromArbitrumToEthereum + } +} + +/// Setup the environment for the tests +/// +/// This function first creates a PreTestingContext struct, which allows setting up the program test context, and load in accounts before starting the test context. +/// Then it starts the test context and returns a TestingContext struct. +/// +/// # Arguments +/// +/// * `shim_mode` - The mode of the shim +/// * `transfer_direction` - The direction of the transfer +/// * `vaa_args` - The arguments for the VAA +/// +/// # Returns +/// +/// A TestingContext struct containing the testing actors, test context, loaded fixture accounts, +/// and testing state (which includes the auction state and the VAAs) pub async fn setup_environment( shim_mode: ShimMode, transfer_direction: TransferDirection, diff --git a/solana/programs/matching-engine/tests/utils/testing_engine.rs b/solana/programs/matching-engine/tests/utils/testing_engine.rs new file mode 100644 index 000000000..1970ff4c6 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/testing_engine.rs @@ -0,0 +1,237 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::shimless::{ + initialize::{initialize_program, InitializeFixture}, + make_offer::{improve_offer, place_initial_offer_shimless}, +}; + +use super::{ + auction::AuctionAccounts, + router::{create_all_router_endpoints_test, TestRouterEndpoints}, + setup::TestingContext, + testing_engine_configs::*, +}; + +#[allow(dead_code)] +pub enum InstructionTrigger { + InitializeProgram(InitializeInstructionConfig), + CreateCctpRouterEndpoints, + InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), + CloseFastMarketOrderShim, + PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), + PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), + ImproveOfferShimless(ImproveOfferInstructionConfig), + ExecuteOrderShimless, + ExecuteOrderShim, + PrepareOrderShimless, + PrepareOrderShim, + SettleAuction, +} + +pub struct InstructionTriggerResults { + pub initialize_program: Option, + pub create_cctp_router_endpoints: Option, +} + +impl InstructionTriggerResults { + pub fn new() -> Self { + Self { + initialize_program: None, + create_cctp_router_endpoints: None, + } + } +} + +pub struct TestingEngine { + pub testing_context: Rc>, + pub instruction_triggers: Vec, + pub instruction_trigger_results: Rc>, +} + +impl TestingEngine { + pub async fn new( + testing_context: TestingContext, + instruction_triggers: Vec, + ) -> Self { + Self { + testing_context: Rc::new(RefCell::new(testing_context)), + instruction_triggers, + instruction_trigger_results: Rc::new(RefCell::new(InstructionTriggerResults::new())), + } + } + + pub async fn execute(&self) { + for trigger in self.instruction_triggers.iter() { + self.execute_trigger(trigger).await; + } + } + + async fn execute_trigger(&self, trigger: &InstructionTrigger) { + match trigger { + InstructionTrigger::InitializeProgram(config) => { + self.instruction_trigger_results + .borrow_mut() + .initialize_program = self.initialize_program(config).await; + } + InstructionTrigger::CreateCctpRouterEndpoints => { + self.instruction_trigger_results + .borrow_mut() + .create_cctp_router_endpoints = self.create_cctp_router_endpoints().await; + } + InstructionTrigger::PlaceInitialOfferShimless(config) => { + self.place_initial_offer_shimless(config).await; + } + InstructionTrigger::ImproveOfferShimless(config) => { + self.improve_offer_shimless(config).await; + } + _ => panic!("Not implemented yet"), // Not implemented yet + } + } + + async fn initialize_program( + &self, + config: &InitializeInstructionConfig, + ) -> Option { + let auction_parameters_config = config.auction_parameters_config.clone(); + let expected_error = config.expected_error.as_ref(); + + let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { + let testing_context_ref = self.testing_context.borrow(); + let result = initialize_program( + &testing_context_ref, + auction_parameters_config, + expected_error, + ) + .await; + + let testing_actors = &testing_context_ref.testing_actors; + ( + result, + testing_actors.owner.pubkey(), + testing_actors.owner_assistant.pubkey(), + testing_actors + .fee_recipient + .token_account_address() + .unwrap(), + ) + }; + + if expected_error.is_none() { + let initialize_fixture = result.expect("Failed to initialize program"); + initialize_fixture.verify_custodian( + owner_pubkey, + owner_assistant_pubkey, + fee_recipient_token_account, + ); + + let auction_config_address = initialize_fixture.get_auction_config_address(); + self.testing_context + .borrow_mut() + .testing_state + .program_state + .initialize(auction_config_address); + return Some(initialize_fixture); + } + None + } + + async fn create_cctp_router_endpoints(&self) -> Option { + let custodian_address = self + .testing_context + .borrow() + .testing_state + .program_state + .get_custodian_address(); + let testing_actors = &self.testing_context.borrow().testing_actors; + let result = create_all_router_endpoints_test( + &self.testing_context.borrow(), + testing_actors.owner.pubkey(), + custodian_address, + testing_actors.owner.keypair(), + ) + .await; + Some(result) + } + + async fn place_initial_offer_shimless(&self, config: &PlaceInitialOfferInstructionConfig) { + let solver = + self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + let expected_error = config.expected_error.as_ref(); + let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); + let fast_vaa = testing_context + .get_vaa_pair(0) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); + let custodian_address = testing_context + .testing_state + .program_state + .get_custodian_address(); + let auction_config_address = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .auction_config_address; + + let auction_accounts = AuctionAccounts::new( + Some(fast_vaa_pubkey), + solver, + auction_config_address, + self.instruction_trigger_results + .borrow() + .create_cctp_router_endpoints + .as_ref() + .unwrap(), + custodian_address, + testing_context.get_usdc_mint_address(), + testing_context.testing_state.transfer_direction, + ); + place_initial_offer_shimless( + testing_context, + &auction_accounts, + fast_vaa, + testing_context.get_matching_engine_program_id(), + expected_error, + ) + .await; + if expected_error.is_none() { + self.testing_context + .borrow() + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .verify_initial_offer(&self.testing_context.borrow().test_context) + .await; + } + } + + async fn improve_offer_shimless(&self, config: &ImproveOfferInstructionConfig) { + let expected_error = config.expected_error.as_ref(); + let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); + let solver = + self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + let offer_price = config.offer_price; + let auction_config_address = testing_context + .testing_state + .auction_state + .get_active_auction() + .unwrap() + .auction_config_address; + improve_offer( + testing_context, + testing_context.get_matching_engine_program_id(), + solver, + auction_config_address, + offer_price, + expected_error, + ) + .await; + // TODO: Implement check on improved offer auction state + // auction_state + // .borrow() + // .verify_improved_offer(&testing_context.test_context) + // .await; + } +} diff --git a/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs b/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs new file mode 100644 index 000000000..e6a5600fc --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs @@ -0,0 +1,82 @@ +use crate::shimless::initialize::AuctionParametersConfig; +use matching_engine::error::MatchingEngineError; + +#[derive(Clone)] +pub struct ExpectedError { + pub instruction_index: u8, + pub error: MatchingEngineError, +} + +#[derive(Clone)] +pub struct InitializeInstructionConfig { + pub auction_parameters_config: AuctionParametersConfig, + pub expected_error: Option, +} + +impl Default for InitializeInstructionConfig { + fn default() -> Self { + Self { + auction_parameters_config: AuctionParametersConfig::default(), + expected_error: None, + } + } +} + +#[derive(Clone)] +pub struct InitializeFastMarketOrderShimInstructionConfig { + pub fast_market_order_config: FastMarketOrderConfig, + pub expected_error: Option, +} + +impl Default for InitializeFastMarketOrderShimInstructionConfig { + fn default() -> Self { + Self { + fast_market_order_config: FastMarketOrderConfig::default(), + expected_error: None, + } + } +} + +pub struct PlaceInitialOfferInstructionConfig { + pub solver_index: usize, + pub offer_price: u64, + pub expected_error: Option, +} + +impl Default for PlaceInitialOfferInstructionConfig { + fn default() -> Self { + Self { + solver_index: 0, + offer_price: 1__000_000, + expected_error: None, + } + } +} + +pub struct ImproveOfferInstructionConfig { + pub solver_index: usize, + pub offer_price: u64, + pub expected_error: Option, +} + +impl Default for ImproveOfferInstructionConfig { + fn default() -> Self { + Self { + solver_index: 0, + offer_price: 500_000, + expected_error: None, + } + } +} +#[derive(Clone)] +pub struct FastMarketOrderConfig { + pub fast_market_order_id: u32, +} + +impl Default for FastMarketOrderConfig { + fn default() -> Self { + Self { + fast_market_order_id: 0, + } + } +} diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 4834597ed..9ff751ba9 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -293,13 +293,13 @@ impl TestVaaPair { vaa_nonce: u32, sequence: u64, cctp_mint_recipient: Pubkey, - create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, + create_deposit_and_fast_transfer_params: &CreateDepositAndFastTransferParams, is_posted: bool, ) -> Self { create_deposit_and_fast_transfer_params.verify(); - let deposit_params = create_deposit_and_fast_transfer_params.deposit_params; + let deposit_params = &create_deposit_and_fast_transfer_params.deposit_params; let create_fast_transfer_params = - create_deposit_and_fast_transfer_params.fast_transfer_params; + &create_deposit_and_fast_transfer_params.fast_transfer_params; let (deposit_vaa_pubkey, deposit_vaa_data, deposit) = create_deposit_message( token_mint, source_address.clone(), @@ -509,12 +509,13 @@ impl TestVaaPairs { destination_address: ChainAddress, cctp_mint_recipient: Pubkey, vaa_args: &VaaArgs, - create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, ) { let sequence = vaa_args.sequence.unwrap_or(self.len() as u64); let cctp_nonce = vaa_args.cctp_nonce.unwrap_or(sequence + 1); let vaa_nonce = vaa_args.vaa_nonce.unwrap_or(0); let is_posted = vaa_args.post_vaa; + let create_deposit_and_fast_transfer_params = + &vaa_args.create_deposit_and_fast_transfer_params; let test_fast_transfer = TestVaaPair::new( vaa_args.start_timestamp, token_mint, @@ -541,7 +542,6 @@ impl TestVaaPairs { source_address: [u8; 32], destination_address: [u8; 32], vaa_args: &VaaArgs, - create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, ) { let source_address = ChainAddress::new_with_address(source_chain, source_address); let destination_address = @@ -554,7 +554,6 @@ impl TestVaaPairs { destination_address, cctp_mint_recipient, vaa_args, - create_deposit_and_fast_transfer_params, ); if vaa_args.post_vaa { for test_fast_transfer in self.0.iter() { @@ -578,6 +577,7 @@ pub struct VaaArgs { pub vaa_nonce: Option, pub start_timestamp: Option, pub post_vaa: bool, + pub create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, } impl Default for VaaArgs { @@ -588,6 +588,7 @@ impl Default for VaaArgs { vaa_nonce: None, start_timestamp: None, post_vaa: false, + create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams::default(), } } } @@ -603,7 +604,6 @@ pub fn create_vaas_test_with_chain_and_address( vaa_args: VaaArgs, ) -> TestVaaPairs { let mut test_fast_transfers = TestVaaPairs::new(); - let create_deposit_and_fast_transfer_params = CreateDepositAndFastTransferParams::default(); test_fast_transfers.create_vaas_with_chain_and_address( program_test, mint_address, @@ -613,7 +613,6 @@ pub fn create_vaas_test_with_chain_and_address( source_address, destination_address, &vaa_args, - create_deposit_and_fast_transfer_params, ); test_fast_transfers } From 0dfbb0e3ba2ca580e0973044614b9a828d0a8832 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 25 Mar 2025 11:12:11 +0000 Subject: [PATCH 024/112] all tests pass --- .../processor/prepare_order_response.rs | 8 +- .../tests/initialize_integration_tests.rs | 105 ++++-- .../matching-engine/tests/shimful/shims.rs | 13 +- .../tests/shimful/shims_execute_order.rs | 6 +- .../shimful/shims_prepare_order_response.rs | 8 +- .../tests/shimless/execute_order.rs | 16 +- .../tests/shimless/make_offer.rs | 63 ++-- .../tests/shimless/settle_auction.rs | 12 +- .../tests/testing_engine/config.rs | 40 +- .../tests/testing_engine/engine.rs | 348 ++++++++++++------ .../tests/testing_engine/state.rs | 254 +++++++++++++ .../matching-engine/tests/utils/auction.rs | 19 +- .../matching-engine/tests/utils/router.rs | 133 +++---- .../matching-engine/tests/utils/setup.rs | 31 +- .../matching-engine/tests/utils/vaa.rs | 1 + 15 files changed, 714 insertions(+), 343 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index c8c2a2cc7..8041b9790 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -227,7 +227,8 @@ pub fn prepare_order_response_cctp_shim( let finalized_vaa_message = data.finalized_vaa_message; // Load accounts let fast_market_order_zero_copy = - FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..]) + .map(Box::new)?; // Create pdas for addresses that need to be created // Check the prepared order response account is valid // TODO: Pass the digest so it isn't recomputed @@ -240,7 +241,7 @@ pub fn prepare_order_response_cctp_shim( let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; let finalised_vaa_consistency_level = fast_market_order_zero_copy.vaa_consistency_level; - &finalized_vaa_message.digest( + finalized_vaa_message.digest( finalised_vaa_sequence, finalised_vaa_timestamp, finalised_vaa_emitter_chain, @@ -277,7 +278,6 @@ pub fn prepare_order_response_cctp_shim( let slow_order_response = liquidity_layer_message .slow_order_response() .ok_or_else(|| MatchingEngineError::InvalidDepositPayloadId)?; - let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, &fast_market_order_digest, @@ -385,7 +385,7 @@ pub fn prepare_order_response_cctp_shim( &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, ); data.push(guardian_set_bump); - data.extend_from_slice(finalized_vaa_message_digest); + data.extend_from_slice(&finalized_vaa_message_digest); data }; diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs index 9e47aec14..ba6457c32 100644 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ b/solana/programs/matching-engine/tests/initialize_integration_tests.rs @@ -6,6 +6,8 @@ use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use shimless::execute_order::execute_order_shimless_test; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; +use testing_engine::config::CreateCctpRouterEndpointsInstructionConfig; +use utils::constants; mod shimful; mod shimless; mod testing_engine; @@ -22,7 +24,7 @@ use shimful::shims::{ use shimful::shims_execute_order::execute_order_fallback_test; use shimless::initialize::{initialize_program, AuctionParametersConfig}; use shimless::make_offer::{improve_offer, place_initial_offer_shimless}; -use solana_sdk::transaction::VersionedTransaction; +use solana_sdk::transaction::{TransactionError, VersionedTransaction}; use utils::auction::AuctionAccounts; use utils::router::{add_local_router_endpoint_ix, create_all_router_endpoints_test}; use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; @@ -41,13 +43,13 @@ pub async fn test_initialize_program() { let initialize_config = InitializeInstructionConfig::default(); - let testing_engine = TestingEngine::new( - testing_context, - vec![InstructionTrigger::InitializeProgram(initialize_config)], - ) - .await; + let testing_engine = TestingEngine::new(testing_context).await; - testing_engine.execute().await; + testing_engine + .execute(vec![InstructionTrigger::InitializeProgram( + initialize_config, + )]) + .await; } /// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains @@ -62,16 +64,13 @@ pub async fn test_cctp_token_router_endpoint_creation() { let initialize_config = InitializeInstructionConfig::default(); - let testing_engine = TestingEngine::new( - testing_context, - vec![ - InstructionTrigger::InitializeProgram(initialize_config), - InstructionTrigger::CreateCctpRouterEndpoints, - ], - ) - .await; + let testing_engine = TestingEngine::new(testing_context).await; - testing_engine.execute().await; + testing_engine + .execute(vec![InstructionTrigger::InitializeProgram( + initialize_config, + )]) + .await; } #[tokio::test] @@ -112,19 +111,19 @@ pub async fn test_setup_vaas() { testing_context.verify_vaas().await; - let testing_engine = TestingEngine::new( - testing_context, - vec![ + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute(vec![ InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints, + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), InstructionTrigger::PlaceInitialOfferShimless( PlaceInitialOfferInstructionConfig::default(), ), InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), - ], - ) - .await; - testing_engine.execute().await; + ]) + .await; } #[tokio::test] @@ -380,7 +379,7 @@ pub async fn test_place_initial_offer_fallback() { None, ) .await; - let _initial_offer_fixture = place_initial_offer_fallback_test( + let initial_offer_fixture = place_initial_offer_fallback_test( &mut testing_context, &auction_accounts, true, // Expected to pass @@ -390,6 +389,9 @@ pub async fn test_place_initial_offer_fallback() { // Attempt to improve the offer using the non-fallback method with another solver making the improved offer println!("Improving offer"); + let auction_state = initial_offer_fixture + .expect("Failed to get initial offer fixture") + .auction_state; let second_solver = testing_context.testing_actors.solvers[1].clone(); improve_offer( &mut testing_context, @@ -397,6 +399,7 @@ pub async fn test_place_initial_offer_fallback() { second_solver, auction_config_address, 500_000, + &auction_state, None, ) .await; @@ -440,14 +443,19 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { // Now test without the fallback program let mut auction_accounts = initial_offer_fallback_fixture.auction_accounts; auction_accounts.fast_vaa = Some(first_test_ft.get_vaa_pubkey()); + + let offer_price = 1__000_000; + let transaction_error = TransactionError::AccountInUse; place_initial_offer_shimless( &mut testing_context, &auction_accounts, - first_test_ft, + &first_test_ft, + offer_price, PROGRAM_ID, Some(&ExpectedError { instruction_index: 0, - error: MatchingEngineError::AccountAlreadyInitialized, + error_code: 0, // This is the error code for account in use + error_string: transaction_error.to_string(), }), // Expected to fail ) .await; @@ -480,10 +488,12 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { ) .await; // Place initial offer using the shimless instruction + let offer_price = 1__000_000; place_initial_offer_shimless( &mut testing_context, &auction_accounts, - first_test_ft, + &first_test_ft, + offer_price, PROGRAM_ID, None, // Expected to pass ) @@ -585,17 +595,23 @@ pub async fn test_execute_order_shimless() { Some(first_test_fast_transfer_pubkey), ) .await; - place_initial_offer_shimless( + let offer_price = 1__000_000; + let auction_state = place_initial_offer_shimless( &mut testing_context, &auction_accounts, - first_test_fast_transfer, + &first_test_fast_transfer, + offer_price, PROGRAM_ID, None, // Expected to pass ) .await; - - let execute_order_fixture = - execute_order_shimless_test(&mut testing_context, &auction_accounts, true).await; + let execute_order_fixture = execute_order_shimless_test( + &mut testing_context, + &auction_accounts, + &auction_state, + None, + ) + .await; assert!(execute_order_fixture.is_some()); } pub async fn test_execute_order_fallback_blocks_shimless() { @@ -642,9 +658,19 @@ pub async fn test_execute_order_fallback_blocks_shimless() { ) .await .expect("Failed to execute order"); - - let shimless_execute_order_fixture = - execute_order_shimless_test(&mut testing_context, &auction_accounts, false).await; + let auction_state = initial_offer_fallback_fixture.auction_state; + let expected_error = Some(ExpectedError { + instruction_index: 0, + error_code: MatchingEngineError::AccountAlreadyInitialized.into(), + error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), + }); + let shimless_execute_order_fixture = execute_order_shimless_test( + &mut testing_context, + &auction_accounts, + &auction_state, + expected_error, + ) + .await; assert!(shimless_execute_order_fixture.is_none()); } @@ -713,7 +739,6 @@ pub async fn test_prepare_order_shim_fallback() { ) .await .expect("Failed to execute order"); - shimful::shims_prepare_order_response::prepare_order_response_test( &testing_context.test_context, &payer_signer, @@ -769,11 +794,13 @@ pub async fn test_settle_auction_complete() { // Try making initial offer using the shim instruction let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_config_address = initialize_fixture.get_auction_config_address(); + let router_config = CreateCctpRouterEndpointsInstructionConfig::default(); let router_endpoints = create_all_router_endpoints_test( &testing_context, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), testing_context.testing_actors.owner.keypair(), + router_config.chains, ) .await; @@ -833,8 +860,8 @@ pub async fn test_settle_auction_complete() { &execute_order_fixture, &initial_offer_fixture, &initialize_fixture, - &router_endpoints.ethereum.endpoint_address, - &router_endpoints.arbitrum.endpoint_address, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, &usdc_mint_address, &CCTP_MINT_RECIPIENT, &initialize_fixture.get_custodian_address(), diff --git a/solana/programs/matching-engine/tests/shimful/shims.rs b/solana/programs/matching-engine/tests/shimful/shims.rs index 0669b2513..df0c74922 100644 --- a/solana/programs/matching-engine/tests/shimful/shims.rs +++ b/solana/programs/matching-engine/tests/shimful/shims.rs @@ -1,3 +1,5 @@ +use crate::utils::auction::AuctionState; + use super::super::utils; use super::super::utils::setup::TestingContext; use super::super::utils::{constants::*, setup::Solver}; @@ -413,7 +415,7 @@ fn generate_expected_guardian_signatures_info( // TODO: Separate this into a different file pub struct PlaceInitialOfferShimFixture { - pub auction_state: Rc>, + pub auction_state: AuctionState, pub guardian_set_pubkey: Pubkey, pub guardian_signatures_pubkey: Pubkey, pub fast_market_order_address: Pubkey, @@ -577,10 +579,9 @@ pub async fn place_initial_offer_fallback( offer_price, }, }; - testing_context.testing_state.auction_state = - utils::auction::AuctionState::Active(new_active_auction_state.clone()); + let new_auction_state = utils::auction::AuctionState::Active(new_active_auction_state); Some(PlaceInitialOfferShimFixture { - auction_state: Rc::new(RefCell::new(new_active_auction_state)), + auction_state: new_auction_state, guardian_set_pubkey, guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), fast_market_order_address: fast_market_order_account, @@ -663,7 +664,7 @@ pub async fn close_fast_market_order_fallback( pub fn create_fast_market_order_state_from_vaa_data( vaa_data: &utils::vaa::PostedVaaData, - refund_recipient: Pubkey, + close_account_refund_recipient: Pubkey, ) -> (FastMarketOrderState, utils::vaa::PostedVaaData) { let vaa_data = utils::vaa::PostedVaaData { consistency_level: vaa_data.consistency_level, @@ -711,7 +712,7 @@ pub fn create_fast_market_order_state_from_vaa_data( order.max_fee, order.init_auction_fee, redeemer_message_fixed_length, - refund_recipient.to_bytes(), + close_account_refund_recipient.to_bytes(), vaa_data.sequence, vaa_data.vaa_time, vaa_data.nonce, diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index e942a02c8..f5371e9cc 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -61,11 +61,13 @@ impl ExecuteOrderFallbackAccounts { fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, active_auction: place_initial_offer_fixture .auction_state - .borrow() + .get_active_auction() + .unwrap() .auction_address, active_auction_custody_token: place_initial_offer_fixture .auction_state - .borrow() + .get_active_auction() + .unwrap() .auction_custody_token_address, active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, diff --git a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs index 72907fc85..cd1dc4503 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs @@ -307,8 +307,8 @@ pub async fn prepare_order_response_test( execute_order_fixture: &ExecuteOrderFallbackFixture, initial_offer_fixture: &PlaceInitialOfferShimFixture, initialize_fixture: &InitializeFixture, - eth_endpoint_address: &Pubkey, - arb_endpoint_address: &Pubkey, + to_endpoint_address: &Pubkey, + from_endpoint_address: &Pubkey, usdc_mint_address: &Pubkey, cctp_mint_recipient: &Pubkey, custodian_address: &Pubkey, @@ -368,8 +368,8 @@ pub async fn prepare_order_response_test( &execute_order_fixture, &initial_offer_fixture, &initialize_fixture, - ð_endpoint_address, - &arb_endpoint_address, + &from_endpoint_address, + &to_endpoint_address, &usdc_mint_address, &cctp_message_decoded, &guardian_set_pubkey, diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs index 5c0cd210f..3f3245eb0 100644 --- a/solana/programs/matching-engine/tests/shimless/execute_order.rs +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -1,5 +1,6 @@ +use crate::testing_engine::config::ExpectedError; use crate::utils::account_fixtures::FixtureAccounts; -use crate::utils::auction::AuctionAccounts; +use crate::utils::auction::{AuctionAccounts, AuctionState}; use crate::utils::setup::{TestingContext, TransferDirection}; use anchor_lang::prelude::*; use anchor_lang::{InstructionData, ToAccountMetas}; @@ -23,12 +24,9 @@ pub fn create_execute_order_shimless_accounts( testing_context: &mut TestingContext, fixture_accounts: &FixtureAccounts, auction_accounts: &AuctionAccounts, + auction_state: &AuctionState, ) -> ExecuteOrderShimlessAccounts { - let active_auction_state = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap(); + let active_auction_state = auction_state.get_active_auction().unwrap(); let active_auction_address = active_auction_state.auction_address; let active_auction_custody_token = active_auction_state.auction_custody_token_address; let cctp_message = Pubkey::find_program_address( @@ -149,7 +147,8 @@ pub fn create_execute_order_shimless_accounts( pub async fn execute_order_shimless_test( testing_context: &mut TestingContext, auction_accounts: &AuctionAccounts, - expected_to_pass: bool, + auction_state: &AuctionState, + expected_error: Option, ) -> Option { crate::utils::setup::fast_forward_slots(&testing_context.test_context, 3).await; let fixture_accounts = testing_context @@ -160,6 +159,7 @@ pub async fn execute_order_shimless_test( testing_context, &fixture_accounts, auction_accounts, + auction_state, ); let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); let execute_order_ix = Instruction { @@ -184,7 +184,7 @@ pub async fn execute_order_shimless_test( .banks_client .process_transaction(tx) .await; - if expected_to_pass { + if expected_error.is_none() { assert!(tx_result.is_ok()); Some(ExecuteOrderShimlessFixture {}) } else { diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs index dc4544bd8..e0305e9d7 100644 --- a/solana/programs/matching-engine/tests/shimless/make_offer.rs +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -22,12 +22,13 @@ use utils::setup::{Solver, TestingContext}; use utils::vaa::TestVaa; pub async fn place_initial_offer_shimless( - testing_context: &mut TestingContext, + testing_context: &TestingContext, accounts: &AuctionAccounts, - fast_market_order: TestVaa, + fast_market_order: &TestVaa, + offer_price: u64, program_id: Pubkey, expected_error: Option<&ExpectedError>, -) { +) -> AuctionState { let test_ctx = &testing_context.test_context; let owner_keypair = testing_context.testing_actors.owner.keypair(); let auction_address = Pubkey::find_program_address( @@ -43,9 +44,7 @@ pub async fn place_initial_offer_shimless( &program_id, ) .0; - let initial_offer_ix = PlaceInitialOfferCctpIx { - offer_price: 1__000_000, - }; + let initial_offer_ix = PlaceInitialOfferCctpIx { offer_price }; let fast_order_path = FastOrderPath { fast_vaa: LiquidityLayerVaa { @@ -148,7 +147,7 @@ pub async fn place_initial_offer_shimless( // If the transaction failed and we expected it to pass, we would not get here if expected_error.is_none() { - testing_context.testing_state.auction_state = AuctionState::Active(ActiveAuctionState { + AuctionState::Active(ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: accounts.auction_config, @@ -160,27 +159,26 @@ pub async fn place_initial_offer_shimless( offer_token: accounts.offer_token, offer_price: initial_offer_ix.offer_price, }, - }); - }; + }) + } else { + AuctionState::Inactive + } } pub async fn improve_offer( - testing_context: &mut TestingContext, + testing_context: &TestingContext, program_id: Pubkey, solver: Solver, auction_config: Pubkey, offer_price: u64, + initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, -) { +) -> Option { let test_ctx = &testing_context.test_context; let owner_keypair = testing_context.testing_actors.owner.keypair(); - let auction_state = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap(); - let auction_address = auction_state.auction_address; - let auction_custody_token_address = auction_state.auction_custody_token_address; + let active_auction_state = initial_auction_state.get_active_auction().unwrap(); + let auction_address = active_auction_state.auction_address; + let auction_custody_token_address = active_auction_state.auction_custody_token_address; let improve_offer_ix = ImproveOfferIx { offer_price }; @@ -203,7 +201,7 @@ pub async fn improve_offer( auction: auction_address, custody_token: auction_custody_token_address, config: auction_config, - best_offer_token: auction_state.best_offer.offer_token, + best_offer_token: active_auction_state.best_offer.offer_token, }; let improve_offer_accounts = ImproveOfferAccounts { transfer_authority, @@ -216,7 +214,7 @@ pub async fn improve_offer( let mut account_metas = improve_offer_accounts.to_account_metas(None); for meta in account_metas.iter_mut() { - if meta.pubkey == auction_state.best_offer.offer_token { + if meta.pubkey == active_auction_state.best_offer.offer_token { meta.is_writable = true; } } @@ -241,14 +239,21 @@ pub async fn improve_offer( // If the transaction failed and we expected it to pass, we would not get here if expected_error.is_none() { - let auction_state = testing_context - .testing_state - .auction_state - .get_active_auction_mut() - .unwrap(); - auction_state.best_offer = AuctionOffer { - offer_token, - offer_price: improve_offer_ix.offer_price, - }; + let initial_offer = &initial_auction_state + .get_active_auction() + .unwrap() + .initial_offer; + Some(AuctionState::Active(ActiveAuctionState { + auction_address, + auction_custody_token_address, + auction_config_address: auction_config, + initial_offer: initial_offer.clone(), + best_offer: AuctionOffer { + offer_token, + offer_price, + }, + })) + } else { + None } } diff --git a/solana/programs/matching-engine/tests/shimless/settle_auction.rs b/solana/programs/matching-engine/tests/shimless/settle_auction.rs index b7cd81211..422a13744 100644 --- a/solana/programs/matching-engine/tests/shimless/settle_auction.rs +++ b/solana/programs/matching-engine/tests/shimless/settle_auction.rs @@ -1,5 +1,6 @@ +use crate::utils::auction::AuctionState; + use super::super::shimful::*; -use super::super::utils; use anchor_lang::prelude::*; use anchor_lang::InstructionData; use anchor_spl::token::spl_token; @@ -18,9 +19,12 @@ pub async fn settle_auction_complete( payer_signer: &Rc, usdc_mint_address: &Pubkey, prepare_order_response_shim_fixture: &shims_prepare_order_response::PrepareOrderResponseShimFixture, - auction_state: &Rc>, + auction_state: &AuctionState, matching_engine_program_id: &Pubkey, ) -> Result<()> { + let active_auction = auction_state + .get_active_auction() + .expect("Failed to get active auction"); let base_fee_token = usdc_mint_address.clone(); let event_seeds = EVENT_AUTHORITY_SEED; let event_authority = @@ -30,8 +34,8 @@ pub async fn settle_auction_complete( base_fee_token: base_fee_token, prepared_order_response: prepare_order_response_shim_fixture.prepared_order_response, prepared_custody_token: prepare_order_response_shim_fixture.prepared_custody_token, - auction: auction_state.borrow().auction_address, - best_offer_token: auction_state.borrow().best_offer.offer_token, + auction: active_auction.auction_address, + best_offer_token: active_auction.best_offer.offer_token, token_program: spl_token::ID, event_authority: event_authority, program: *matching_engine_program_id, diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs index e6a5600fc..9ffecb110 100644 --- a/solana/programs/matching-engine/tests/testing_engine/config.rs +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -1,10 +1,13 @@ -use crate::shimless::initialize::AuctionParametersConfig; -use matching_engine::error::MatchingEngineError; +use std::collections::HashSet; + +use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; +use anchor_lang::prelude::*; #[derive(Clone)] pub struct ExpectedError { pub instruction_index: u8, - pub error: MatchingEngineError, + pub error_code: u32, + pub error_string: String, } #[derive(Clone)] @@ -22,16 +25,31 @@ impl Default for InitializeInstructionConfig { } } +pub struct CreateCctpRouterEndpointsInstructionConfig { + pub chains: HashSet, + pub expected_error: Option, +} + +impl Default for CreateCctpRouterEndpointsInstructionConfig { + fn default() -> Self { + Self { + chains: HashSet::from([Chain::Ethereum, Chain::Arbitrum, Chain::Solana]), + expected_error: None, + } + } +} #[derive(Clone)] pub struct InitializeFastMarketOrderShimInstructionConfig { - pub fast_market_order_config: FastMarketOrderConfig, + pub fast_market_order_id: u32, + pub close_account_refund_recipient: Pubkey, pub expected_error: Option, } impl Default for InitializeFastMarketOrderShimInstructionConfig { fn default() -> Self { Self { - fast_market_order_config: FastMarketOrderConfig::default(), + fast_market_order_id: 0, + close_account_refund_recipient: Pubkey::new_unique(), expected_error: None, } } @@ -68,15 +86,3 @@ impl Default for ImproveOfferInstructionConfig { } } } -#[derive(Clone)] -pub struct FastMarketOrderConfig { - pub fast_market_order_id: u32, -} - -impl Default for FastMarketOrderConfig { - fn default() -> Self { - Self { - fast_market_order_id: 0, - } - } -} diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index ea31a2589..e47327b12 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -1,22 +1,22 @@ -use std::{cell::RefCell, rc::Rc}; +use matching_engine::state::FastMarketOrder; +use solana_sdk::transaction::VersionedTransaction; -use super::config::*; -use crate::shimless::{ - initialize::{initialize_program, InitializeFixture}, - make_offer::{improve_offer, place_initial_offer_shimless}, +use super::{config::*, state::*}; +use crate::shimful::shims::{ + create_fast_market_order_state_from_vaa_data, create_guardian_signatures, + initialise_fast_market_order_fallback_instruction, }; use crate::utils::{ - auction::AuctionAccounts, - router::{create_all_router_endpoints_test, TestRouterEndpoints}, - setup::TestingContext, + auction::AuctionAccounts, router::create_all_router_endpoints_test, setup::TestingContext, }; +use crate::{shimless, utils::vaa::TestVaaPairs}; +use anchor_lang::prelude::*; #[allow(dead_code)] pub enum InstructionTrigger { InitializeProgram(InitializeInstructionConfig), - CreateCctpRouterEndpoints, + CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig), InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), - CloseFastMarketOrderShim, PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), ImproveOfferShimless(ImproveOfferInstructionConfig), @@ -25,85 +25,88 @@ pub enum InstructionTrigger { PrepareOrderShimless, PrepareOrderShim, SettleAuction, -} - -pub struct InstructionTriggerResults { - pub initialize_program: Option, - pub create_cctp_router_endpoints: Option, -} - -impl InstructionTriggerResults { - pub fn new() -> Self { - Self { - initialize_program: None, - create_cctp_router_endpoints: None, - } - } + CloseFastMarketOrderShim, } pub struct TestingEngine { - pub testing_context: Rc>, - pub instruction_triggers: Vec, - pub instruction_trigger_results: Rc>, + pub testing_context: TestingContext, } impl TestingEngine { - pub async fn new( - testing_context: TestingContext, - instruction_triggers: Vec, - ) -> Self { + pub async fn new(testing_context: TestingContext) -> Self { Self { - testing_context: Rc::new(RefCell::new(testing_context)), - instruction_triggers, - instruction_trigger_results: Rc::new(RefCell::new(InstructionTriggerResults::new())), + testing_context: testing_context, } } - pub async fn execute(&self) { - for trigger in self.instruction_triggers.iter() { - self.execute_trigger(trigger).await; + pub async fn execute(&self, instruction_chain: Vec) { + let mut current_state = self.create_initial_state(); + + for trigger in instruction_chain { + current_state = self.execute_trigger(¤t_state, &trigger).await; } } - async fn execute_trigger(&self, trigger: &InstructionTrigger) { + async fn execute_trigger( + &self, + current_state: &TestingEngineState, + trigger: &InstructionTrigger, + ) -> TestingEngineState { match trigger { InstructionTrigger::InitializeProgram(config) => { - self.instruction_trigger_results - .borrow_mut() - .initialize_program = self.initialize_program(config).await; + self.initialize_program(current_state, config).await } - InstructionTrigger::CreateCctpRouterEndpoints => { - self.instruction_trigger_results - .borrow_mut() - .create_cctp_router_endpoints = self.create_cctp_router_endpoints().await; + InstructionTrigger::CreateCctpRouterEndpoints(config) => { + self.create_cctp_router_endpoints(current_state, config) + .await } InstructionTrigger::PlaceInitialOfferShimless(config) => { - self.place_initial_offer_shimless(config).await; + self.place_initial_offer_shimless(current_state, config) + .await + } + InstructionTrigger::InitializeFastMarketOrderShim(config) => { + self.create_fast_market_order_account(current_state, config) + .await } InstructionTrigger::ImproveOfferShimless(config) => { - self.improve_offer_shimless(config).await; + self.improve_offer_shimless(current_state, config).await } _ => panic!("Not implemented yet"), // Not implemented yet } } + pub fn create_initial_state(&self) -> TestingEngineState { + let fixture_accounts = self + .testing_context + .fixture_accounts + .clone() + .expect("Failed to get fixture accounts"); + let vaas: TestVaaPairs = self.testing_context.testing_state.vaas.clone(); + let transfer_direction = self.testing_context.testing_state.transfer_direction; + TestingEngineState::Uninitialized(BaseState { + fixture_accounts, + vaas, + transfer_direction, + }) + } + async fn initialize_program( &self, + initial_state: &TestingEngineState, config: &InitializeInstructionConfig, - ) -> Option { + ) -> TestingEngineState { let auction_parameters_config = config.auction_parameters_config.clone(); let expected_error = config.expected_error.as_ref(); let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { - let testing_context_ref = self.testing_context.borrow(); - let result = initialize_program( - &testing_context_ref, + let result = shimless::initialize::initialize_program( + &self.testing_context, auction_parameters_config, expected_error, ) .await; - let testing_actors = &testing_context_ref.testing_actors; + let testing_actors = &self.testing_context.testing_actors; ( result, testing_actors.owner.pubkey(), @@ -124,113 +127,218 @@ impl TestingEngine { ); let auction_config_address = initialize_fixture.get_auction_config_address(); - self.testing_context - .borrow_mut() - .testing_state - .program_state - .initialize(auction_config_address); - return Some(initialize_fixture); + return TestingEngineState::Initialized { + base: initial_state.base().clone(), + initialized: InitializedState { + auction_config_address, + custodian_address: initialize_fixture.get_custodian_address(), + }, + }; } - None + initial_state.clone() } - async fn create_cctp_router_endpoints(&self) -> Option { - let custodian_address = self - .testing_context - .borrow() - .testing_state - .program_state - .get_custodian_address(); - let testing_actors = &self.testing_context.borrow().testing_actors; + async fn create_cctp_router_endpoints( + &self, + current_state: &TestingEngineState, + config: &CreateCctpRouterEndpointsInstructionConfig, + ) -> TestingEngineState { + // Make sure testing state is at least initialized + let initialized_state = current_state + .initialized() + .expect("Testing state is not initialized"); + let custodian_address = initialized_state.custodian_address; + let testing_actors = &self.testing_context.testing_actors; let result = create_all_router_endpoints_test( - &self.testing_context.borrow(), + &self.testing_context, testing_actors.owner.pubkey(), custodian_address, testing_actors.owner.keypair(), + config.chains.clone(), ) .await; - Some(result) + TestingEngineState::RouterEndpointsCreated { + base: current_state.base().clone(), + initialized: initialized_state.clone(), + router_endpoints: RouterEndpointsState { endpoints: result }, + } } - async fn place_initial_offer_shimless(&self, config: &PlaceInitialOfferInstructionConfig) { - let solver = - self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + async fn create_fast_market_order_account( + &self, + current_state: &TestingEngineState, + config: &InitializeFastMarketOrderShimInstructionConfig, + ) -> TestingEngineState { + let first_test_vaa_pair = current_state.get_first_test_vaa_pair(); + let fast_transfer_vaa = first_test_vaa_pair.fast_transfer_vaa.clone(); + let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data( + &fast_transfer_vaa.vaa_data, + config.close_account_refund_recipient, + ); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = + create_guardian_signatures( + &self.testing_context.test_context, + &self.testing_context.testing_actors.owner.keypair(), + &vaa_data, + &BaseState::CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await; + + let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrder::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.close_account_refund_recipient, + ], + &self.testing_context.get_matching_engine_program_id(), + ); + + let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + &self.testing_context.testing_actors.owner.keypair(), + &self.testing_context.get_matching_engine_program_id(), + fast_market_order, + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ); + + let recent_blockhash = self.testing_context.test_context.borrow().last_blockhash; + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( + &[initialise_fast_market_order_ix], + Some(&self.testing_context.testing_actors.owner.pubkey()), + &[&self.testing_context.testing_actors.owner.keypair()], + recent_blockhash, + ); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + let res = self + .testing_context + .test_context + .borrow_mut() + .banks_client + .process_transaction(versioned_transaction) + .await; + if config.expected_error.is_none() { + res.expect("Failed to initialise fast market order"); + TestingEngineState::FastMarketOrderAccountCreated { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: FastMarketOrderAccountCreatedState { + fast_market_order_address: fast_market_order_account, + fast_market_order_bump: fast_market_order_bump, + }, + } + } else { + current_state.clone() + } + } + async fn place_initial_offer_shimless( + &self, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + ) -> TestingEngineState { + assert!( + current_state.router_endpoints().is_some(), + "Router endpoints are not created" + ); + let solver = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index"); let expected_error = config.expected_error.as_ref(); - let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); - let fast_vaa = testing_context - .get_vaa_pair(0) + let fast_vaa = ¤t_state + .base() + .vaas + .get(0) .expect("Failed to get vaa pair") .fast_transfer_vaa; let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); - let custodian_address = testing_context - .testing_state - .program_state - .get_custodian_address(); - let auction_config_address = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap() + let auction_config_address = current_state + .initialized() + .expect("Testing state is not initialized") .auction_config_address; - + let custodian_address = current_state + .initialized() + .expect("Testing state is not initialized") + .custodian_address; let auction_accounts = AuctionAccounts::new( Some(fast_vaa_pubkey), - solver, + solver.clone(), auction_config_address, - self.instruction_trigger_results - .borrow() - .create_cctp_router_endpoints - .as_ref() - .unwrap(), + ¤t_state + .router_endpoints() + .expect("Router endpoints are not created") + .endpoints, custodian_address, - testing_context.get_usdc_mint_address(), - testing_context.testing_state.transfer_direction, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, ); - place_initial_offer_shimless( - testing_context, + let auction_state = shimless::make_offer::place_initial_offer_shimless( + &self.testing_context, &auction_accounts, fast_vaa, - testing_context.get_matching_engine_program_id(), + config.offer_price, + self.testing_context.get_matching_engine_program_id(), expected_error, ) .await; if expected_error.is_none() { - self.testing_context - .borrow() - .testing_state - .auction_state + auction_state .get_active_auction() .unwrap() - .verify_initial_offer(&self.testing_context.borrow().test_context) + .verify_initial_offer(&self.testing_context.test_context) .await; + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state, + }; } + current_state.clone() } - async fn improve_offer_shimless(&self, config: &ImproveOfferInstructionConfig) { + async fn improve_offer_shimless( + &self, + current_state: &TestingEngineState, + config: &ImproveOfferInstructionConfig, + ) -> TestingEngineState { let expected_error = config.expected_error.as_ref(); - let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); - let solver = - self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); + let solver = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index"); let offer_price = config.offer_price; - let auction_config_address = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap() - .auction_config_address; - improve_offer( - testing_context, - testing_context.get_matching_engine_program_id(), - solver, + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let new_auction_state = shimless::make_offer::improve_offer( + &self.testing_context, + self.testing_context.get_matching_engine_program_id(), + solver.clone(), auction_config_address, offer_price, + current_state.auction_state(), expected_error, ) .await; - // TODO: Implement check on improved offer auction state - // auction_state - // .borrow() - // .verify_improved_offer(&testing_context.test_context) - // .await; + if expected_error.is_none() { + let auction_state = new_auction_state.unwrap(); + return TestingEngineState::OfferImproved { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state, + }; + } + current_state.clone() } } diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/programs/matching-engine/tests/testing_engine/state.rs index e69de29bb..a737a2fc8 100644 --- a/solana/programs/matching-engine/tests/testing_engine/state.rs +++ b/solana/programs/matching-engine/tests/testing_engine/state.rs @@ -0,0 +1,254 @@ +use crate::utils::{ + account_fixtures::FixtureAccounts, + auction::AuctionState, + router::TestRouterEndpoints, + setup::TransferDirection, + vaa::{TestVaaPair, TestVaaPairs}, +}; +use anchor_lang::prelude::*; +use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; +use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; + +// Base state containing common data +#[derive(Clone)] +pub struct BaseState { + pub fixture_accounts: FixtureAccounts, + pub vaas: TestVaaPairs, + pub transfer_direction: TransferDirection, +} + +impl BaseState { + pub const CCTP_MINT_RECIPIENT: Pubkey = CCTP_MINT_RECIPIENT; + pub const CORE_BRIDGE_PROGRAM_ID: Pubkey = CORE_BRIDGE_PROGRAM_ID; + + pub fn get_matching_engine_program_id(&self) -> Pubkey { + PROGRAM_ID + } +} + +// Each state contains its specific data +#[derive(Clone)] +pub struct InitializedState { + pub auction_config_address: Pubkey, + pub custodian_address: Pubkey, +} + +#[derive(Clone)] +pub struct RouterEndpointsState { + pub endpoints: TestRouterEndpoints, +} + +#[derive(Clone)] +pub struct FastMarketOrderAccountCreatedState { + pub fast_market_order_address: Pubkey, + pub fast_market_order_bump: u8, +} + +#[derive(Clone)] +pub struct InitialOfferPlacedState { + pub auction_state: AuctionState, +} + +#[derive(Clone)] +pub struct OfferImprovedState { + pub auction_state: AuctionState, +} + +#[derive(Clone)] +pub struct OrderExecutedState { + pub auction_state: AuctionState, +} + +#[derive(Clone)] +pub struct OrderPreparedState { + pub prepared_order_address: Pubkey, +} + +// The main state enum that reflects all possible instruction states +#[derive(Clone)] +pub enum TestingEngineState { + Uninitialized(BaseState), + Initialized { + base: BaseState, + initialized: InitializedState, + }, + RouterEndpointsCreated { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + }, + FastMarketOrderAccountCreated { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + fast_market_order: FastMarketOrderAccountCreatedState, + }, + InitialOfferPlaced { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + fast_market_order: Option, + auction_state: AuctionState, + }, + OfferImproved { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + fast_market_order: Option, + auction_state: AuctionState, + }, + OrderExecuted { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + fast_market_order: Option, + auction_state: AuctionState, + }, + OrderPrepared { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + fast_market_order: Option, + auction_state: AuctionState, + order_prepared: OrderPreparedState, + }, + AuctionSettled { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + auction_state: AuctionState, + fast_market_order: Option, + order_prepared: OrderPreparedState, + }, + FastMarketOrderClosed { + base: BaseState, + initialized: InitializedState, + router_endpoints: RouterEndpointsState, + auction_state: AuctionState, + fast_market_order: Option, + order_prepared: Option, + }, +} + +// Implement accessors for common data +impl TestingEngineState { + // Base state accessor + pub fn base(&self) -> &BaseState { + match self { + Self::Uninitialized(state) => state, + Self::Initialized { base, .. } => base, + Self::RouterEndpointsCreated { base, .. } => base, + Self::FastMarketOrderAccountCreated { base, .. } => base, + Self::InitialOfferPlaced { base, .. } => base, + Self::OfferImproved { base, .. } => base, + Self::OrderExecuted { base, .. } => base, + Self::OrderPrepared { base, .. } => base, + Self::AuctionSettled { base, .. } => base, + Self::FastMarketOrderClosed { base, .. } => base, + } + } + + // Initialization data accessor + pub fn initialized(&self) -> Option<&InitializedState> { + match self { + Self::Uninitialized(_) => None, + Self::Initialized { initialized, .. } => Some(initialized), + Self::RouterEndpointsCreated { initialized, .. } => Some(initialized), + Self::FastMarketOrderAccountCreated { initialized, .. } => Some(initialized), + Self::InitialOfferPlaced { initialized, .. } => Some(initialized), + Self::OfferImproved { initialized, .. } => Some(initialized), + Self::OrderExecuted { initialized, .. } => Some(initialized), + Self::OrderPrepared { initialized, .. } => Some(initialized), + Self::AuctionSettled { initialized, .. } => Some(initialized), + Self::FastMarketOrderClosed { initialized, .. } => Some(initialized), + } + } + + // Router endpoints accessor + pub fn router_endpoints(&self) -> Option<&RouterEndpointsState> { + match self { + Self::Uninitialized(_) | Self::Initialized { .. } => None, + Self::RouterEndpointsCreated { + router_endpoints, .. + } => Some(router_endpoints), + Self::FastMarketOrderAccountCreated { + router_endpoints, .. + } => Some(router_endpoints), + Self::InitialOfferPlaced { + router_endpoints, .. + } => Some(router_endpoints), + Self::OfferImproved { + router_endpoints, .. + } => Some(router_endpoints), + Self::OrderExecuted { + router_endpoints, .. + } => Some(router_endpoints), + Self::OrderPrepared { + router_endpoints, .. + } => Some(router_endpoints), + Self::AuctionSettled { + router_endpoints, .. + } => Some(router_endpoints), + Self::FastMarketOrderClosed { + router_endpoints, .. + } => Some(router_endpoints), + } + } + + // Fast market order accessor + pub fn fast_market_order(&self) -> Option<&FastMarketOrderAccountCreatedState> { + match self { + Self::FastMarketOrderAccountCreated { + fast_market_order, .. + } => Some(fast_market_order), + Self::InitialOfferPlaced { + fast_market_order, .. + } => fast_market_order.as_ref(), + Self::OfferImproved { + fast_market_order, .. + } => fast_market_order.as_ref(), + Self::OrderExecuted { + fast_market_order, .. + } => fast_market_order.as_ref(), + Self::AuctionSettled { + fast_market_order, .. + } => fast_market_order.as_ref(), + _ => None, + } + } + + // Auction state accessor + pub fn auction_state(&self) -> &AuctionState { + match self { + Self::InitialOfferPlaced { auction_state, .. } => auction_state, + Self::OfferImproved { auction_state, .. } => auction_state, + Self::OrderExecuted { auction_state, .. } => auction_state, + Self::OrderPrepared { auction_state, .. } => auction_state, + Self::AuctionSettled { auction_state, .. } => auction_state, + _ => &AuctionState::Inactive, + } + } + + // Prepared order accessor + pub fn prepared_order(&self) -> Option<&OrderPreparedState> { + match self { + Self::OrderPrepared { order_prepared, .. } => Some(order_prepared), + Self::AuctionSettled { order_prepared, .. } => Some(order_prepared), + Self::FastMarketOrderClosed { order_prepared, .. } => order_prepared.as_ref(), + _ => None, + } + } + + pub fn get_first_test_vaa_pair(&self) -> &TestVaaPair { + self.base().vaas.get(0).unwrap() + } + + // Convenience methods for common fields + pub fn custodian_address(&self) -> Option { + self.initialized().map(|state| state.custodian_address) + } + + pub fn auction_config_address(&self) -> Option { + self.initialized().map(|state| state.auction_config_address) + } +} diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 0ba5f2feb..579159b6b 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -3,9 +3,11 @@ use anchor_lang::prelude::*; use super::super::shimless; use super::router::TestRouterEndpoints; use super::setup::{Solver, TransferDirection}; +use super::Chain; use matching_engine::state::{Auction, AuctionInfo}; use solana_program_test::ProgramTestContext; use std::cell::RefCell; +use std::collections::HashSet; use std::rc::Rc; #[derive(Clone)] @@ -20,6 +22,7 @@ pub struct AuctionAccounts { pub usdc_mint: Pubkey, } +#[derive(Clone)] pub enum AuctionState { Active(ActiveAuctionState), Inactive, @@ -32,13 +35,6 @@ impl AuctionState { AuctionState::Inactive => None, } } - - pub fn get_active_auction_mut(&mut self) -> Option<&mut ActiveAuctionState> { - match self { - AuctionState::Active(auction) => Some(auction), - AuctionState::Inactive => None, - } - } } #[derive(Clone)] pub struct ActiveAuctionState { @@ -67,12 +63,12 @@ impl AuctionAccounts { ) -> Self { let (from_router_endpoint, to_router_endpoint) = match direction { TransferDirection::FromEthereumToArbitrum => ( - router_endpoints.ethereum.endpoint_address, - router_endpoints.arbitrum.endpoint_address, + router_endpoints.get_endpoint_address(Chain::Ethereum), + router_endpoints.get_endpoint_address(Chain::Arbitrum), ), TransferDirection::FromArbitrumToEthereum => ( - router_endpoints.arbitrum.endpoint_address, - router_endpoints.ethereum.endpoint_address, + router_endpoints.get_endpoint_address(Chain::Arbitrum), + router_endpoints.get_endpoint_address(Chain::Ethereum), ), }; Self { @@ -100,6 +96,7 @@ impl AuctionAccounts { testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), testing_context.testing_actors.owner.keypair(), + HashSet::from([Chain::Ethereum, Chain::Arbitrum, Chain::Solana]), ) .await; diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 17e2fb5cb..e6176ce6e 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -24,6 +24,9 @@ use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; use solana_sdk::transaction::VersionedTransaction; use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::HashSet; +use std::ops::Deref; use std::rc::Rc; fn generate_admin(owner_or_assistant: Pubkey, custodian: Pubkey) -> Admin { @@ -107,41 +110,29 @@ impl TestEndpointInfo { } } -pub struct TestRouterEndpoints { - pub arbitrum: TestRouterEndpoint, - pub ethereum: TestRouterEndpoint, - pub solana: TestRouterEndpoint, -} +#[derive(Clone)] +pub struct TestRouterEndpoints(HashMap); -impl TestRouterEndpoints { - pub fn new( - arbitrum: TestRouterEndpoint, - ethereum: TestRouterEndpoint, - solana: TestRouterEndpoint, - ) -> Self { - Self { - arbitrum, - ethereum, - solana, - } +impl Deref for TestRouterEndpoints { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 } +} +impl TestRouterEndpoints { #[allow(dead_code)] pub fn get_endpoint_info(&self, chain: Chain) -> TestEndpointInfo { - match chain { - Chain::Arbitrum => self.arbitrum.info.clone(), - Chain::Ethereum => self.ethereum.info.clone(), - Chain::Solana => self.solana.info.clone(), - _ => panic!("Unsupported chain"), - } + self.get(&chain).unwrap().info.clone() } #[allow(dead_code)] pub fn get_endpoint_address(&self, chain: Chain) -> Pubkey { match chain { - Chain::Arbitrum => self.arbitrum.endpoint_address, - Chain::Ethereum => self.ethereum.endpoint_address, - Chain::Solana => self.solana.endpoint_address, + Chain::Arbitrum => self.get(&Chain::Arbitrum).unwrap().endpoint_address, + Chain::Ethereum => self.get(&Chain::Ethereum).unwrap().endpoint_address, + Chain::Solana => self.get(&Chain::Solana).unwrap().endpoint_address, _ => panic!("Unsupported chain"), } } @@ -372,48 +363,36 @@ pub async fn add_local_router_endpoint_ix( test_router_endpoint } -pub async fn create_cctp_router_endpoints_test( +pub async fn create_cctp_router_endpoint( testing_context: &TestingContext, admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, -) -> [TestRouterEndpoint; 2] { + chain: Chain, +) -> TestRouterEndpoint { let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let program_id = testing_context.get_matching_engine_program_id(); - let arb_remote_token_messenger = fixture_accounts.arbitrum_remote_token_messenger; let test_context = &testing_context.test_context; - let arb_chain = Chain::Arbitrum; - let arbitrum_token_router_endpoint = add_cctp_router_endpoint_ix( - test_context, - admin_owner_or_assistant, - custodian_address, - admin_keypair.as_ref(), - program_id, - arb_remote_token_messenger, - usdc_mint_address, - arb_chain, - ) - .await; - - let eth_chain = Chain::Ethereum; - let eth_remote_token_messenger = fixture_accounts.ethereum_remote_token_messenger; - let ethereum_token_router_endpoint = add_cctp_router_endpoint_ix( + let token_messenger = match chain { + Chain::Arbitrum => fixture_accounts.arbitrum_remote_token_messenger, + Chain::Ethereum => fixture_accounts.ethereum_remote_token_messenger, + _ => { + panic!("Unsupported chain"); + } + }; + let token_router_endpoint = add_cctp_router_endpoint_ix( test_context, admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), program_id, - eth_remote_token_messenger, + token_messenger, usdc_mint_address, - eth_chain, + chain, ) .await; - - [ - arbitrum_token_router_endpoint, - ethereum_token_router_endpoint, - ] + token_router_endpoint } pub async fn create_all_router_endpoints_test( @@ -421,28 +400,38 @@ pub async fn create_all_router_endpoints_test( admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, + chains: HashSet, ) -> TestRouterEndpoints { - let [arbitrum_token_router_endpoint, ethereum_token_router_endpoint] = - create_cctp_router_endpoints_test( - testing_context, - admin_owner_or_assistant.clone(), - custodian_address.clone(), - admin_keypair.clone(), - ) - .await; - - let local_token_router_endpoint = add_local_router_endpoint_ix( - testing_context, - admin_owner_or_assistant, - custodian_address, - admin_keypair.as_ref(), - ) - .await; - TestRouterEndpoints::new( - arbitrum_token_router_endpoint, - ethereum_token_router_endpoint, - local_token_router_endpoint, - ) + let mut endpoints: HashMap = HashMap::new(); + for chain in chains { + match chain { + Chain::Solana => { + let local_token_router_endpoint = add_local_router_endpoint_ix( + testing_context, + admin_owner_or_assistant, + custodian_address, + admin_keypair.as_ref(), + ) + .await; + endpoints.insert(chain, local_token_router_endpoint); + } + Chain::Arbitrum | Chain::Ethereum => { + let cctp_router_endpoint = create_cctp_router_endpoint( + testing_context, + admin_owner_or_assistant, + custodian_address, + admin_keypair.clone(), + chain, + ) + .await; + endpoints.insert(chain, cctp_router_endpoint); + } + _ => { + panic!("Unsupported chain"); + } + } + } + TestRouterEndpoints(endpoints) } pub async fn get_remote_token_messenger( diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 1a8777513..df2bc9b6a 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -219,7 +219,7 @@ impl TestingContext { if let Some(expected_error) = expected_error { let tx_error = tx_result.expect_err(&format!( "Expected error {:?}, but transaction succeeded", - expected_error.error + expected_error.error_string )); match tx_error { @@ -232,18 +232,16 @@ impl TestingContext { "Expected error on instruction {}, but got: {:?}", expected_error.instruction_index, tx_error ); - let expected_error_code = u32::from(expected_error.error); - assert_eq!( - error_code, expected_error_code, + error_code, expected_error.error_code, "Program returned error code {}, expected {} ({:?})", - error_code, expected_error_code, expected_error.error + error_code, expected_error.error_code, expected_error.error_string ); } _ => { panic!( "Expected program error {:?}, but got: {:?}", - expected_error.error, tx_error + expected_error.error_string, tx_error ); } } @@ -504,32 +502,12 @@ pub async fn fast_forward_slots(test_context: &Rc>, println!("Fast forwarded {} slots", num_slots); } -#[derive(Clone)] -pub enum ProgramState { - Initialized(ProgramAddresses), - Uninitialized, -} - -impl ProgramState { - pub fn initialize(&mut self, custodian_address: Pubkey) { - *self = ProgramState::Initialized(ProgramAddresses { custodian_address }); - } - - pub fn get_custodian_address(&self) -> Pubkey { - match self { - ProgramState::Initialized(addresses) => addresses.custodian_address, - ProgramState::Uninitialized => panic!("Program is not initialized"), - } - } -} - #[derive(Clone)] pub struct ProgramAddresses { pub custodian_address: Pubkey, } pub struct TestingState { - pub program_state: ProgramState, pub auction_state: AuctionState, pub vaas: TestVaaPairs, pub transfer_direction: TransferDirection, @@ -538,7 +516,6 @@ pub struct TestingState { impl Default for TestingState { fn default() -> Self { Self { - program_state: ProgramState::Uninitialized, auction_state: AuctionState::Inactive, vaas: TestVaaPairs::new(), transfer_direction: TransferDirection::FromEthereumToArbitrum, diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 9ff751ba9..22feaff46 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -479,6 +479,7 @@ pub fn create_fast_transfer_message( (vaa_address, posted_vaa_data, fast_market_order) } +#[derive(Clone)] pub struct TestVaaPairs(pub Vec); impl Deref for TestVaaPairs { From 8e8498db70dc506a241003bc0fdd34479d988cd8 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 25 Mar 2025 18:42:05 +0000 Subject: [PATCH 025/112] fully refactored, all tests pass --- .../processor/prepare_order_response.rs | 4 +- .../src/state/fast_market_order.rs | 15 + .../tests/initialize_integration_tests.rs | 883 ------------------ .../tests/integration_tests.rs | 517 ++++++++++ .../matching-engine/tests/shimful/shims.rs | 110 +-- .../tests/shimful/shims_execute_order.rs | 58 +- .../shimful/shims_prepare_order_response.rs | 129 +-- .../tests/shimless/execute_order.rs | 39 +- .../tests/shimless/make_offer.rs | 20 +- .../tests/shimless/settle_auction.rs | 36 +- .../tests/testing_engine/config.rs | 83 +- .../tests/testing_engine/engine.rs | 392 +++++++- .../tests/testing_engine/state.rs | 68 +- .../matching-engine/tests/utils/auction.rs | 40 +- .../matching-engine/tests/utils/vaa.rs | 4 + 15 files changed, 1220 insertions(+), 1178 deletions(-) delete mode 100644 solana/programs/matching-engine/tests/initialize_integration_tests.rs create mode 100644 solana/programs/matching-engine/tests/integration_tests.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 8041b9790..c2099208f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -226,9 +226,9 @@ pub fn prepare_order_response_cctp_shim( let receive_message_args = data.to_receive_message_args(); let finalized_vaa_message = data.finalized_vaa_message; // Load accounts + let fast_market_order_account_data = fast_market_order.data.borrow(); let fast_market_order_zero_copy = - FastMarketOrderState::try_deserialize(&mut &fast_market_order.data.borrow()[..]) - .map(Box::new)?; + FastMarketOrderState::try_read(&fast_market_order_account_data[..])?; // Create pdas for addresses that need to be created // Check the prepared order response account is valid // TODO: Pass the digest so it isn't recomputed diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 1a4a59738..b639e570d 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::*; +use anchor_lang::Discriminator; use solana_program::keccak; /// An account that represents a fast market order vaa. It is created by the signer of the transaction, and owned by the matching engine program. @@ -139,4 +140,18 @@ impl FastMarketOrder { .try_into() .unwrap() } + + /// Read from an account info + pub fn try_read(data: &[u8]) -> Result<&Self> { + if data.len() < 8 { + return Err(ErrorCode::AccountDiscriminatorNotFound.into()); + } + let discriminator: [u8; 8] = data[0..8].try_into().unwrap(); + if discriminator != Self::discriminator() { + return Err(ErrorCode::AccountDiscriminatorMismatch.into()); + } + let byte_muck_data = &data[8..]; + let fast_market_order = bytemuck::from_bytes::(byte_muck_data); + Ok(fast_market_order) + } } diff --git a/solana/programs/matching-engine/tests/initialize_integration_tests.rs b/solana/programs/matching-engine/tests/initialize_integration_tests.rs deleted file mode 100644 index ba6457c32..000000000 --- a/solana/programs/matching-engine/tests/initialize_integration_tests.rs +++ /dev/null @@ -1,883 +0,0 @@ -use anchor_lang::AccountDeserialize; -use anchor_spl::token::TokenAccount; -use matching_engine::error::MatchingEngineError; -use matching_engine::state::FastMarketOrder; -use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; -use shimless::execute_order::execute_order_shimless_test; -use solana_program_test::tokio; -use solana_sdk::pubkey::Pubkey; -use testing_engine::config::CreateCctpRouterEndpointsInstructionConfig; -use utils::constants; -mod shimful; -mod shimless; -mod testing_engine; -mod utils; -use crate::testing_engine::config::{ - ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, - PlaceInitialOfferInstructionConfig, -}; -use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; -use shimful::shims::{ - initialise_fast_market_order_fallback_instruction, place_initial_offer_fallback, - place_initial_offer_fallback_test, set_up_post_message_transaction_test, -}; -use shimful::shims_execute_order::execute_order_fallback_test; -use shimless::initialize::{initialize_program, AuctionParametersConfig}; -use shimless::make_offer::{improve_offer, place_initial_offer_shimless}; -use solana_sdk::transaction::{TransactionError, VersionedTransaction}; -use utils::auction::AuctionAccounts; -use utils::router::{add_local_router_endpoint_ix, create_all_router_endpoints_test}; -use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; -use utils::vaa::VaaArgs; -use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; - -/// Test that the program is initialised correctly -#[tokio::test] -pub async fn test_initialize_program() { - let testing_context = setup_environment( - ShimMode::None, - TransferDirection::FromArbitrumToEthereum, - None, // Vaa args for creating vaas - ) - .await; - - let initialize_config = InitializeInstructionConfig::default(); - - let testing_engine = TestingEngine::new(testing_context).await; - - testing_engine - .execute(vec![InstructionTrigger::InitializeProgram( - initialize_config, - )]) - .await; -} - -/// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains -#[tokio::test] -pub async fn test_cctp_token_router_endpoint_creation() { - let testing_context = setup_environment( - ShimMode::None, // Shim mode - TransferDirection::FromArbitrumToEthereum, // Transfer direction - None, // Vaa args - ) - .await; - - let initialize_config = InitializeInstructionConfig::default(); - - let testing_engine = TestingEngine::new(testing_context).await; - - testing_engine - .execute(vec![InstructionTrigger::InitializeProgram( - initialize_config, - )]) - .await; -} - -#[tokio::test] -pub async fn test_local_token_router_endpoint_creation() { - let testing_context = setup_environment( - ShimMode::None, - TransferDirection::FromArbitrumToEthereum, - None, - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - - let _local_token_router_endpoint = add_local_router_endpoint_ix( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair().as_ref(), - ) - .await; -} - -// Test setting up vaas -// Vaa is from arbitrum to ethereum -// - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs -#[tokio::test] -pub async fn test_setup_vaas() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = - setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; - - testing_context.verify_vaas().await; - - let testing_engine = TestingEngine::new(testing_context).await; - testing_engine - .execute(vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless( - PlaceInitialOfferInstructionConfig::default(), - ), - InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), - ]) - .await; -} - -#[tokio::test] -pub async fn test_post_message_shims() { - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - None, - ) - .await; - let actors = testing_context.testing_actors; - let emitter_signer = actors.owner.keypair(); - let payer_signer = actors.solvers[0].keypair(); - set_up_post_message_transaction_test( - &testing_context.test_context, - &payer_signer, - &emitter_signer, - ) - .await; -} - -#[tokio::test] -pub async fn test_initialise_fast_market_order_fallback() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let solver = testing_context.testing_actors.solvers[0].clone(); - - let vaa_data = first_test_ft.vaa_data; - let (fast_market_order, vaa_data) = - shimful::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - shimful::shims::create_guardian_signatures( - &testing_context.test_context, - &testing_context.testing_actors.owner.keypair(), - &vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - None, - ) - .await; - - let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( - &testing_context.testing_actors.owner.keypair(), - &PROGRAM_ID, - fast_market_order, - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, - ); - let recent_blockhash = testing_context.test_context.borrow().last_blockhash; - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( - &[initialise_fast_market_order_ix], - Some(&testing_context.testing_actors.owner.pubkey()), - &[&testing_context.testing_actors.owner.keypair()], - recent_blockhash, - ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - testing_context - .test_context - .borrow_mut() - .banks_client - .process_transaction(versioned_transaction) - .await - .expect("Failed to initialise fast market order"); -} - -#[tokio::test] -pub async fn test_close_fast_market_order_fallback() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let solver = testing_context.testing_actors.solvers[0].clone(); - - let vaa_data = first_test_ft.vaa_data; - let (fast_market_order, vaa_data) = - shimful::shims::create_fast_market_order_state_from_vaa_data(&vaa_data, solver.pubkey()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - shimful::shims::create_guardian_signatures( - &testing_context.test_context, - &testing_context.testing_actors.owner.keypair(), - &vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - None, - ) - .await; - - let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( - &testing_context.testing_actors.owner.keypair(), - &PROGRAM_ID, - fast_market_order, - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, - ); - let recent_blockhash = testing_context.test_context.borrow().last_blockhash; - // Get balance of solver before initialising fast market order - let solver_balance_before = testing_context - .test_context - .borrow_mut() - .banks_client - .get_balance(solver.pubkey()) - .await - .expect("Failed to get balance of solver"); - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( - &[initialise_fast_market_order_ix], - Some(&testing_context.testing_actors.owner.pubkey()), - &[&testing_context.testing_actors.owner.keypair()], - recent_blockhash, - ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - testing_context - .test_context - .borrow_mut() - .banks_client - .process_transaction(versioned_transaction) - .await - .expect("Failed to initialise fast market order"); - let fast_market_order_account = Pubkey::find_program_address( - &[ - FastMarketOrder::SEED_PREFIX, - &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient, - ], - &PROGRAM_ID, - ) - .0; - shimful::shims::close_fast_market_order_fallback( - &testing_context.test_context, - &solver.keypair(), - &PROGRAM_ID, - &fast_market_order_account, - ) - .await; - let solver_balance_after = testing_context - .test_context - .borrow_mut() - .banks_client - .get_balance(solver.pubkey()) - .await - .expect("Failed to get balance of solver"); - assert!(solver_balance_after > solver_balance_before, "Solver balance before initialising fast market order was {:?}, but after closing it was {:?}, though it should have been greater", solver_balance_before, solver_balance_after); -} - -#[tokio::test] -pub async fn test_approve_usdc() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let vaa_data = first_test_ft.vaa_data; - - let actors = testing_context.testing_actors; - let solver = actors.solvers[0].clone(); - let offer_price: u64 = 1__000_000; - let program_id = PROGRAM_ID; - let new_pubkey = Pubkey::new_unique(); - - let transfer_authority = Pubkey::find_program_address( - &[ - common::TRANSFER_AUTHORITY_SEED_PREFIX, - &new_pubkey.to_bytes(), - &offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; - solver - .approve_usdc( - &testing_context.test_context, - &transfer_authority, - offer_price, - ) - .await; - - let usdc_balance = solver.get_balance(&testing_context.test_context).await; - - // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = - shimful::shims::create_guardian_signatures( - &testing_context.test_context, - &actors.owner.keypair(), - &vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - None, - ) - .await; - - println!("Solver USDC balance: {:?}", usdc_balance); - let solver_token_account_address = solver.token_account_address().unwrap(); - let solver_token_account_info = testing_context - .test_context - .borrow_mut() - .banks_client - .get_account(solver_token_account_address) - .await - .expect("Failed to query banks client for solver token account info") - .expect("Failed to get solver token account info"); - let solver_token_account = - TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); - assert!(solver_token_account.delegate.is_some()); -} - -#[tokio::test] -// Testing a initial offer from arbitrum to ethereum -// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction -pub async fn test_place_initial_offer_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - None, - ) - .await; - let initial_offer_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, - true, // Expected to pass - ) - .await; - let auction_config_address = initialize_fixture.get_auction_config_address(); - - // Attempt to improve the offer using the non-fallback method with another solver making the improved offer - println!("Improving offer"); - let auction_state = initial_offer_fixture - .expect("Failed to get initial offer fixture") - .auction_state; - let second_solver = testing_context.testing_actors.solvers[1].clone(); - improve_offer( - &mut testing_context, - PROGRAM_ID, - second_solver, - auction_config_address, - 500_000, - &auction_state, - None, - ) - .await; - println!("Offer improved"); - // improved_offer_fixture.verify_improved_offer(&testing_context.test_context).await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_shim_blocks_non_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - None, - ) - .await; - let initial_offer_fallback_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, // Auction accounts have not been created yet - true, // Expected to pass - ) - .await - .expect("Should have been able to place initial offer"); - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - // Now test without the fallback program - let mut auction_accounts = initial_offer_fallback_fixture.auction_accounts; - auction_accounts.fast_vaa = Some(first_test_ft.get_vaa_pubkey()); - - let offer_price = 1__000_000; - let transaction_error = TransactionError::AccountInUse; - place_initial_offer_shimless( - &mut testing_context, - &auction_accounts, - &first_test_ft, - offer_price, - PROGRAM_ID, - Some(&ExpectedError { - instruction_index: 0, - error_code: 0, // This is the error code for account in use - error_string: transaction_error.to_string(), - }), // Expected to fail - ) - .await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_non_shim_blocks_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - Some(first_test_ft.get_vaa_pubkey()), - ) - .await; - // Place initial offer using the shimless instruction - let offer_price = 1__000_000; - place_initial_offer_shimless( - &mut testing_context, - &auction_accounts, - &first_test_ft, - offer_price, - PROGRAM_ID, - None, // Expected to pass - ) - .await; - // Now test with the fallback program (shims) and expect it to fail - let none_initial_offer_fallback_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, - false, // Expected to fail - ) - .await; - assert!(none_initial_offer_fallback_fixture.is_none()); -} - -#[tokio::test] -// Testing an execute order from arbitrum to ethereum -// TODO: Flesh out this test to see if the message was posted correctly -pub async fn test_execute_order_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - None, - ) - .await; - let initial_offer_fallback_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, - true, // Expected to pass - ) - .await - .expect("Should have been able to place initial offer"); - - let solver = testing_context.testing_actors.solvers[0].clone(); - let balance_before_execute_order = solver.get_balance(&testing_context.test_context).await; - println!( - "Solver balance after placing initial offer: {:?}", - balance_before_execute_order - ); - - let _execute_order_fixture = execute_order_fallback_test( - &mut testing_context, - &auction_accounts, - &initial_offer_fallback_fixture, - solver.clone(), - true, // Expected to pass - ) - .await - .expect("Failed to execute order"); - - let balance_after_execute_order = solver.get_balance(&testing_context.test_context).await; - assert!( - balance_after_execute_order > balance_before_execute_order, - "Solver balance after executing order was {:?}, but should have been greater", - balance_after_execute_order - ); -} - -#[tokio::test] -pub async fn test_execute_order_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - - let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let first_test_fast_transfer_pubkey = first_test_fast_transfer.get_vaa_pubkey(); - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - Some(first_test_fast_transfer_pubkey), - ) - .await; - let offer_price = 1__000_000; - let auction_state = place_initial_offer_shimless( - &mut testing_context, - &auction_accounts, - &first_test_fast_transfer, - offer_price, - PROGRAM_ID, - None, // Expected to pass - ) - .await; - let execute_order_fixture = execute_order_shimless_test( - &mut testing_context, - &auction_accounts, - &auction_state, - None, - ) - .await; - assert!(execute_order_fixture.is_some()); -} -pub async fn test_execute_order_fallback_blocks_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let first_test_fast_transfer = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - let auction_accounts = utils::auction::AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - Some(first_test_fast_transfer.get_vaa_pubkey()), - ) - .await; - let initial_offer_fallback_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, - true, // Expected to pass - ) - .await - .expect("Should have been able to place initial offer"); - - let solver = testing_context.testing_actors.solvers[0].clone(); - - // Try executing the order using the fallback program - let _shim_execute_order_fixture = execute_order_fallback_test( - &mut testing_context, - &auction_accounts, - &initial_offer_fallback_fixture, - solver.clone(), - true, // Expected to pass - ) - .await - .expect("Failed to execute order"); - let auction_state = initial_offer_fallback_fixture.auction_state; - let expected_error = Some(ExpectedError { - instruction_index: 0, - error_code: MatchingEngineError::AccountAlreadyInitialized.into(), - error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), - }); - let shimless_execute_order_fixture = execute_order_shimless_test( - &mut testing_context, - &auction_accounts, - &auction_state, - expected_error, - ) - .await; - assert!(shimless_execute_order_fixture.is_none()); -} - -// From ethereum to arbitrum -#[tokio::test] -pub async fn test_prepare_order_shim_fallback() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - - let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); - let payload_deserialized: utils::vaa::PayloadDeserialized = first_vaa_pair - .deposit_vaa - .clone() - .payload_deserialized - .unwrap(); - let deposit = payload_deserialized.get_deposit().unwrap(); - - let fixture_accounts = testing_context - .get_fixture_accounts() - .expect("Pre-made fixture accounts not found"); - - // Try making initial offer using the shim instruction - let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_accounts = AuctionAccounts::create_auction_accounts( - &mut testing_context, - &initialize_fixture, - transfer_direction, - None, - ) - .await; - - // Place initial offer using the fallback program - let initial_offer_fixture = place_initial_offer_fallback_test( - &mut testing_context, - &auction_accounts, - true, // Expected to pass - ) - .await - .expect("Failed to place initial offer"); - - let solver = testing_context.testing_actors.solvers[0].clone(); - - let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; - - let payer_signer = testing_context.testing_actors.owner.keypair(); - - let execute_order_fixture = execute_order_fallback_test( - &mut testing_context, - &auction_accounts, - &initial_offer_fixture, - solver.clone(), - true, // Expected to pass - ) - .await - .expect("Failed to execute order"); - shimful::shims_prepare_order_response::prepare_order_response_test( - &testing_context.test_context, - &payer_signer, - &deposit_vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - &PROGRAM_ID, - &fixture_accounts, - &execute_order_fixture, - &initial_offer_fixture, - &initialize_fixture, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, - &usdc_mint_address, - &CCTP_MINT_RECIPIENT, - &initialize_fixture.get_custodian_address(), - &deposit, - ) - .await - .expect("Failed to prepare order response"); -} - -#[tokio::test] -pub async fn test_settle_auction_complete() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let mut testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - - let first_vaa_pair = testing_context.get_vaa_pair(0).unwrap(); - - let payload_deserialized: utils::vaa::PayloadDeserialized = first_vaa_pair - .deposit_vaa - .clone() - .payload_deserialized - .unwrap(); - let deposit = payload_deserialized.get_deposit().unwrap(); - - let fixture_accounts = testing_context - .get_fixture_accounts() - .expect("Pre-made fixture accounts not found"); - // Try making initial offer using the shim instruction - let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_config = CreateCctpRouterEndpointsInstructionConfig::default(); - let router_endpoints = create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), - router_config.chains, - ) - .await; - - let solver = testing_context.testing_actors.solvers[0].clone(); - let auction_accounts = AuctionAccounts::new( - None, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey - transfer_direction, - ); - - let fast_transfer_vaa_data = first_vaa_pair.fast_transfer_vaa.vaa_data; - let deposit_vaa_data = first_vaa_pair.deposit_vaa.vaa_data; - - let payer_signer = testing_context.testing_actors.owner.keypair(); - - // Place initial offer using the fallback program - let initial_offer_fixture = place_initial_offer_fallback( - &mut testing_context, - &payer_signer, - &PROGRAM_ID, - &CORE_BRIDGE_PROGRAM_ID, - &fast_transfer_vaa_data, - solver.clone(), - &auction_accounts, - 1__000_000, // 1 USDC (double underscore for decimal separator) - true, - ) - .await - .expect("Failed to place initial offer"); - - println!( - "Solver balance after placing initial offer: {:?}", - solver.get_balance(&testing_context.test_context).await - ); - - let execute_order_fixture = execute_order_fallback_test( - &mut testing_context, - &auction_accounts, - &initial_offer_fixture, - solver.clone(), - true, // Expected to pass - ) - .await - .expect("Failed to execute order"); - let prepare_order_response_shim_fixture = - shimful::shims_prepare_order_response::prepare_order_response_test( - &testing_context.test_context, - &payer_signer, - &deposit_vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - &PROGRAM_ID, - &fixture_accounts, - &execute_order_fixture, - &initial_offer_fixture, - &initialize_fixture, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, - &usdc_mint_address, - &CCTP_MINT_RECIPIENT, - &initialize_fixture.get_custodian_address(), - &deposit, - ) - .await - .expect("Failed to prepare order response"); - let auction_state = initial_offer_fixture.auction_state; - shimless::settle_auction::settle_auction_complete( - &testing_context.test_context, - &payer_signer, - &usdc_mint_address, - &prepare_order_response_shim_fixture, - &auction_state, - &PROGRAM_ID, - ) - .await - .expect("Failed to settle auction"); -} diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs new file mode 100644 index 000000000..2f9d5cbdf --- /dev/null +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -0,0 +1,517 @@ +use anchor_lang::AccountDeserialize; +use anchor_spl::token::TokenAccount; +use matching_engine::error::MatchingEngineError; +use matching_engine::ID as PROGRAM_ID; +use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; +use testing_engine::config::*; +mod shimful; +mod shimless; +mod testing_engine; +mod utils; +use crate::testing_engine::config::{ + ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, + PlaceInitialOfferInstructionConfig, +}; +use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; +use shimful::shims::set_up_post_message_transaction_test; +use shimless::initialize::{initialize_program, AuctionParametersConfig}; +use solana_sdk::transaction::TransactionError; +use utils::router::add_local_router_endpoint_ix; +use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; +use utils::vaa::VaaArgs; +use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; + +/// Test that the program is initialised correctly +#[tokio::test] +pub async fn test_initialize_program() { + let testing_context = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, // Vaa args for creating vaas + ) + .await; + + let initialize_config = InitializeInstructionConfig::default(); + + let testing_engine = TestingEngine::new(testing_context).await; + + testing_engine + .execute(vec![InstructionTrigger::InitializeProgram( + initialize_config, + )]) + .await; +} + +/// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains +#[tokio::test] +pub async fn test_cctp_token_router_endpoint_creation() { + let testing_context = setup_environment( + ShimMode::None, // Shim mode + TransferDirection::FromArbitrumToEthereum, // Transfer direction + None, // Vaa args + ) + .await; + + let initialize_config = InitializeInstructionConfig::default(); + + let testing_engine = TestingEngine::new(testing_context).await; + + testing_engine + .execute(vec![InstructionTrigger::InitializeProgram( + initialize_config, + )]) + .await; +} + +#[tokio::test] +pub async fn test_local_token_router_endpoint_creation() { + let testing_context = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; + + let initialize_fixture = + initialize_program(&testing_context, AuctionParametersConfig::default(), None) + .await + .expect("Failed to initialize program"); + + let _local_token_router_endpoint = add_local_router_endpoint_ix( + &testing_context, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + testing_context.testing_actors.owner.keypair().as_ref(), + ) + .await; +} + +// Test setting up vaas +// Vaa is from arbitrum to ethereum +// - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs +#[tokio::test] +pub async fn test_setup_vaas() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = + setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; + + testing_context.verify_vaas().await; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute(vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless( + PlaceInitialOfferInstructionConfig::default(), + ), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), + ]) + .await; +} + +#[tokio::test] +pub async fn test_post_message_shims() { + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; + let actors = testing_context.testing_actors; + let emitter_signer = actors.owner.keypair(); + let payer_signer = actors.solvers[0].keypair(); + set_up_post_message_transaction_test( + &testing_context.test_context, + &payer_signer, + &emitter_signer, + ) + .await; +} + +#[tokio::test] +pub async fn test_initialise_fast_market_order_fallback() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_close_fast_market_order_fallback() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_approve_usdc() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let vaa_data = first_test_ft.vaa_data; + + let actors = testing_context.testing_actors; + let solver = actors.solvers[0].clone(); + let offer_price: u64 = 1__000_000; + let program_id = PROGRAM_ID; + let new_pubkey = Pubkey::new_unique(); + + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &new_pubkey.to_bytes(), + &offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + solver + .approve_usdc( + &testing_context.test_context, + &transfer_authority, + offer_price, + ) + .await; + + let usdc_balance = solver.get_balance(&testing_context.test_context).await; + + // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. + let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = + shimful::shims::create_guardian_signatures( + &testing_context.test_context, + &actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await; + + println!("Solver USDC balance: {:?}", usdc_balance); + let solver_token_account_address = solver.token_account_address().unwrap(); + let solver_token_account_info = testing_context + .test_context + .borrow_mut() + .banks_client + .get_account(solver_token_account_address) + .await + .expect("Failed to query banks client for solver token account info") + .expect("Failed to get solver token account info"); + let solver_token_account = + TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); + assert!(solver_token_account.delegate.is_some()); +} + +#[tokio::test] +// Testing a initial offer from arbitrum to ethereum +// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction +pub async fn test_place_initial_offer_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig { + solver_index: 1, + ..ImproveOfferInstructionConfig::default() + }), + ]; + + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_place_initial_offer_shim_blocks_non_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + solver_index: 0, + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + solver_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_place_initial_offer_non_shim_blocks_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + solver_index: 0, + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + solver_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +// Testing an execute order from arbitrum to ethereum +// TODO: Flesh out this test to see if the message was posted correctly +pub async fn test_execute_order_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_execute_order_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} +pub async fn test_execute_order_fallback_blocks_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: MatchingEngineError::AccountAlreadyInitialized.into(), + error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), + }), + ..ExecuteOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +// From ethereum to arbitrum +#[tokio::test] +pub async fn test_prepare_order_shim_fallback() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_settle_auction_complete() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} diff --git a/solana/programs/matching-engine/tests/shimful/shims.rs b/solana/programs/matching-engine/tests/shimful/shims.rs index df0c74922..f041c7a11 100644 --- a/solana/programs/matching-engine/tests/shimful/shims.rs +++ b/solana/programs/matching-engine/tests/shimful/shims.rs @@ -1,4 +1,5 @@ -use crate::utils::auction::AuctionState; +use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::state::InitialOfferPlacedState; use super::super::utils; use super::super::utils::setup::TestingContext; @@ -413,53 +414,18 @@ fn generate_expected_guardian_signatures_info( (expected_length, guardian_signatures) } -// TODO: Separate this into a different file -pub struct PlaceInitialOfferShimFixture { - pub auction_state: AuctionState, - pub guardian_set_pubkey: Pubkey, - pub guardian_signatures_pubkey: Pubkey, - pub fast_market_order_address: Pubkey, - pub fast_market_order: FastMarketOrderState, - pub auction_accounts: utils::auction::AuctionAccounts, -} - -pub async fn place_initial_offer_fallback_test( - testing_context: &mut TestingContext, - auction_accounts: &utils::auction::AuctionAccounts, - expected_to_pass: bool, -) -> Option { - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let solver = testing_context.testing_actors.solvers[0].clone(); - let vaa_data = first_test_ft.vaa_data; - - // Place initial offer using the fallback program - let payer_signer = testing_context.testing_actors.owner.keypair(); - place_initial_offer_fallback( - testing_context, - &payer_signer, - &testing_context.get_matching_engine_program_id(), - &testing_context.get_wormhole_program_id(), - &vaa_data, - solver.clone(), - auction_accounts, - 1__000_000, // 1 USDC (double underscore for decimal separator) - expected_to_pass, - ) - .await -} - /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. pub async fn place_initial_offer_fallback( - testing_context: &mut TestingContext, + testing_context: &TestingContext, payer_signer: &Rc, - program_id: &Pubkey, - wormhole_program_id: &Pubkey, vaa_data: &utils::vaa::PostedVaaData, solver: Solver, + fast_market_order_account: &Pubkey, auction_accounts: &utils::auction::AuctionAccounts, offer_price: u64, - expected_to_pass: bool, -) -> Option { + expected_error: Option<&ExpectedError>, +) -> Option { + let program_id = testing_context.get_matching_engine_program_id(); let test_ctx = &testing_context.test_context; let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); @@ -496,31 +462,6 @@ pub async fn place_initial_offer_fallback( let solver_usdc_balance = solver.get_balance(test_ctx).await; println!("Solver USDC balance: {:?}", solver_usdc_balance); - // Create the guardian set and signatures - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - create_guardian_signatures(test_ctx, payer_signer, &vaa_data, wormhole_program_id, None) - .await; - - // Create the fast market order account - let fast_market_order_account = Pubkey::find_program_address( - &[ - FastMarketOrderState::SEED_PREFIX, - &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient, - ], - program_id, - ) - .0; - - let create_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( - payer_signer, - program_id, - fast_market_order, - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, - ); - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( offer_price, vaa_data.sequence, @@ -535,7 +476,7 @@ pub async fn place_initial_offer_fallback( auction_config: &auction_accounts.auction_config, from_endpoint: &auction_accounts.from_router_endpoint, to_endpoint: &auction_accounts.to_router_endpoint, - fast_market_order: &fast_market_order_account, + fast_market_order: fast_market_order_account, auction: &auction_address, offer_token: &auction_accounts.offer_token, auction_custody_token: &auction_custody_token_address, @@ -544,7 +485,7 @@ pub async fn place_initial_offer_fallback( token_program: &anchor_spl::token::spl_token::ID, }; let place_initial_offer_ix = PlaceInitialOfferCctpShimFallback { - program_id: program_id, + program_id: &program_id, accounts: place_initial_offer_ix_accounts, data: place_initial_offer_ix_data, } @@ -553,40 +494,34 @@ pub async fn place_initial_offer_fallback( let recent_blockhash = test_ctx.borrow().last_blockhash; let transaction = Transaction::new_signed_with_payer( - &[create_fast_market_order_ix, place_initial_offer_ix], + &[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], recent_blockhash, ); - let tx_result = test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) + testing_context + .execute_and_verify_transaction(transaction, expected_error) .await; - assert_eq!(tx_result.is_ok(), expected_to_pass); - if tx_result.is_ok() { + if expected_error.is_none() { let new_active_auction_state = utils::auction::ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: auction_accounts.auction_config, initial_offer: utils::auction::AuctionOffer { + participant: payer_signer.pubkey(), offer_token: auction_accounts.offer_token, offer_price, }, best_offer: utils::auction::AuctionOffer { + participant: payer_signer.pubkey(), offer_token: auction_accounts.offer_token, offer_price, }, }; let new_auction_state = utils::auction::AuctionState::Active(new_active_auction_state); - Some(PlaceInitialOfferShimFixture { + Some(InitialOfferPlacedState { auction_state: new_auction_state, - guardian_set_pubkey, - guardian_signatures_pubkey: guardian_signatures_pubkey.clone().to_owned(), - fast_market_order_address: fast_market_order_account, - fast_market_order, - auction_accounts: auction_accounts.clone(), }) } else { None @@ -629,11 +564,13 @@ pub fn initialise_fast_market_order_fallback_instruction( } pub async fn close_fast_market_order_fallback( - test_ctx: &Rc>, + testing_context: &TestingContext, refund_recipient_keypair: &Rc, program_id: &Pubkey, fast_market_order_address: &Pubkey, + expected_error: Option<&ExpectedError>, ) { + let test_ctx = &testing_context.test_context; let recent_blockhash = test_ctx .borrow_mut() .get_new_latest_blockhash() @@ -654,12 +591,9 @@ pub async fn close_fast_market_order_fallback( &[refund_recipient_keypair], recent_blockhash, ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await - .expect("Failed to close fast market order"); + testing_context + .execute_and_verify_transaction(transaction, expected_error) + .await; } pub fn create_fast_market_order_state_from_vaa_data( diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index f5371e9cc..d6b637375 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -1,17 +1,16 @@ +use crate::testing_engine::config::ExpectedError; +use crate::utils::auction::ActiveAuctionState; use crate::utils::setup::TestingContext; use super::super::utils; -use super::shims; use anchor_spl::token::spl_token; use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; -use solana_program_test::ProgramTestContext; use solana_sdk::{ pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction, }; -use std::cell::RefCell; use std::rc::Rc; use utils::setup::TransferDirection; use utils::{constants::*, setup::Solver}; @@ -42,7 +41,8 @@ pub struct ExecuteOrderFallbackAccounts { impl ExecuteOrderFallbackAccounts { pub fn new( auction_accounts: &utils::auction::AuctionAccounts, - place_initial_offer_fixture: &shims::PlaceInitialOfferShimFixture, // Does not need to be place initial offer fixture, can just be fast market order address. Auction state and transfer direction can be taken from testing context + fast_market_order_address: &Pubkey, + active_auction_state: &ActiveAuctionState, signer: &Pubkey, fixture_accounts: &utils::account_fixtures::FixtureAccounts, transfer_direction: TransferDirection, @@ -55,20 +55,13 @@ impl ExecuteOrderFallbackAccounts { fixture_accounts.ethereum_remote_token_messenger } }; + Self { signer: signer.clone(), custodian: auction_accounts.custodian, - fast_market_order_address: place_initial_offer_fixture.fast_market_order_address, - active_auction: place_initial_offer_fixture - .auction_state - .get_active_auction() - .unwrap() - .auction_address, - active_auction_custody_token: place_initial_offer_fixture - .auction_state - .get_active_auction() - .unwrap() - .auction_custody_token_address, + fast_market_order_address: fast_market_order_address.clone(), + active_auction: active_auction_state.auction_address, + active_auction_custody_token: active_auction_state.auction_custody_token_address, active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, @@ -96,14 +89,15 @@ pub struct ExecuteOrderFallbackFixtureAccounts { } pub async fn execute_order_fallback( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, - expected_to_pass: bool, + expected_error: Option<&ExpectedError>, ) -> Option { // Get target chain and use as remote address + let test_ctx = &testing_context.test_context; let cctp_message = Pubkey::find_program_address( &[ common::CCTP_MESSAGE_SEED_PREFIX, @@ -194,16 +188,10 @@ pub async fn execute_order_fallback( &[&payer_signer], recent_blockhash, ); - let transaction_result = test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) + testing_context + .execute_and_verify_transaction(transaction, expected_error) .await; - if expected_to_pass { - assert!( - transaction_result.is_ok(), - "Transaction should have been successful" - ); + if expected_error.is_none() { Some(ExecuteOrderFallbackFixture { cctp_message, post_message_sequence, @@ -217,38 +205,36 @@ pub async fn execute_order_fallback( }, }) } else { - assert!( - transaction_result.is_err(), - "Transaction should have been unsuccessful" - ); None } } pub async fn execute_order_fallback_test( - testing_context: &mut TestingContext, + testing_context: &TestingContext, auction_accounts: &utils::auction::AuctionAccounts, - place_initial_offer_fixture: &shims::PlaceInitialOfferShimFixture, + fast_market_order_address: &Pubkey, + active_auction_state: &ActiveAuctionState, solver: Solver, - expected_to_pass: bool, + expected_error: Option<&ExpectedError>, ) -> Option { let fixture_accounts = testing_context .get_fixture_accounts() .expect("Pre-made fixture accounts not found"); let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( auction_accounts, - place_initial_offer_fixture, + fast_market_order_address, + active_auction_state, &testing_context.testing_actors.owner.pubkey(), &fixture_accounts, testing_context.testing_state.transfer_direction, ); execute_order_fallback( - &testing_context.test_context, + &testing_context, &testing_context.testing_actors.owner.keypair(), &testing_context.get_matching_engine_program_id(), solver, &execute_order_fallback_accounts, - expected_to_pass, + expected_error, ) .await } diff --git a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs index cd1dc4503..04187c6ed 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs @@ -1,7 +1,8 @@ -use super::super::shimless::initialize::InitializeFixture; +use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::state::TestingEngineState; +use crate::utils::setup::{TestingContext, TransferDirection}; + use super::super::utils; -use super::shims::PlaceInitialOfferShimFixture; -use super::shims_execute_order::ExecuteOrderFallbackFixture; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use common::messages::raw::LiquidityLayerDepositMessage; @@ -15,11 +16,10 @@ use matching_engine::fallback::prepare_order_response::{ PrepareOrderResponseCctpShimAccounts, PrepareOrderResponseCctpShimData, }; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; -use solana_program_test::ProgramTestContext; +use matching_engine::CCTP_MINT_RECIPIENT; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; -use std::cell::RefCell; use std::rc::Rc; use utils::account_fixtures::FixtureAccounts; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; @@ -77,9 +77,8 @@ impl PrepareOrderResponseShimAccountsFixture { pub fn new( signer: &Pubkey, fixture_accounts: &FixtureAccounts, - execute_order_fixture: &ExecuteOrderFallbackFixture, - initial_offer_fixture: &PlaceInitialOfferShimFixture, - initialize_fixture: &InitializeFixture, + custodian_address: &Pubkey, + fast_market_order_address: &Pubkey, from_router_endpoint: &Pubkey, to_router_endpoint: &Pubkey, usdc_mint_address: &Pubkey, @@ -98,19 +97,24 @@ impl PrepareOrderResponseShimAccountsFixture { &MESSAGE_TRANSMITTER_PROGRAM_ID, ) .0; + let token_messenger_minter_event_authority = &Pubkey::find_program_address( + &[EVENT_AUTHORITY_SEED], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; let (cctp_used_nonces_pda, _cctp_used_nonces_bump) = UsedNonces::address( cctp_message_decoded.source_domain, cctp_message_decoded.nonce, ); Self { signer: signer.clone(), - custodian: initialize_fixture.get_custodian_address(), - fast_market_order: initial_offer_fixture.fast_market_order_address, + custodian: custodian_address.clone(), + fast_market_order: fast_market_order_address.clone(), from_endpoint: from_router_endpoint.clone(), to_endpoint: to_router_endpoint.clone(), base_fee_token: usdc_mint_address.clone(), // Change this to the solver's address? usdc: usdc_mint_address.clone(), - cctp_mint_recipient: initialize_fixture.addresses.cctp_mint_recipient.clone(), + cctp_mint_recipient: CCTP_MINT_RECIPIENT, cctp_message_transmitter_authority: cctp_message_transmitter_authority.clone(), cctp_message_transmitter_config: fixture_accounts.message_transmitter_config.clone(), cctp_used_nonces: cctp_used_nonces_pda.clone(), @@ -124,9 +128,7 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), cctp_token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, cctp_message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, - cctp_token_messenger_minter_event_authority: execute_order_fixture - .accounts - .token_messenger_minter_event_authority + cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority .clone(), guardian_set: guardian_set.clone(), guardian_set_signatures: guardian_set_signatures.clone(), @@ -187,12 +189,14 @@ impl PrepareOrderResponseShimDataFixture { } pub async fn prepare_order_response_cctp_shim( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, accounts: PrepareOrderResponseShimAccountsFixture, data: PrepareOrderResponseShimDataFixture, matching_engine_program_id: &Pubkey, -) -> Result { + expected_error: Option<&ExpectedError>, +) -> Option { + let test_ctx = &testing_context.test_context; let fast_market_order_digest = data.fast_market_order.digest(); let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, @@ -274,16 +278,17 @@ pub async fn prepare_order_response_cctp_shim( &[&payer_signer], recent_blockhash, ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await - .expect("Failed to process prepare order response cctp shim"); - Ok(PrepareOrderResponseShimFixture { - prepared_order_response: prepared_order_response_pda, - prepared_custody_token: prepared_custody_token_pda, - }) + testing_context + .execute_and_verify_transaction(transaction, expected_error) + .await; + if expected_error.is_none() { + Some(PrepareOrderResponseShimFixture { + prepared_order_response: prepared_order_response_pda, + prepared_custody_token: prepared_custody_token_pda, + }) + } else { + None + } } pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { @@ -298,22 +303,26 @@ pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { } pub async fn prepare_order_response_test( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, deposit_vaa_data: &utils::vaa::PostedVaaData, - core_bridge_program_id: &Pubkey, - matching_engine_program_id: &Pubkey, - fixture_accounts: &FixtureAccounts, - execute_order_fixture: &ExecuteOrderFallbackFixture, - initial_offer_fixture: &PlaceInitialOfferShimFixture, - initialize_fixture: &InitializeFixture, + testing_engine_state: &TestingEngineState, to_endpoint_address: &Pubkey, from_endpoint_address: &Pubkey, - usdc_mint_address: &Pubkey, - cctp_mint_recipient: &Pubkey, - custodian_address: &Pubkey, deposit: &Deposit, -) -> Result { + expected_error: Option<&ExpectedError>, +) -> Option { + let test_ctx = &testing_context.test_context; + let core_bridge_program_id = &testing_context.get_wormhole_program_id(); + let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); + let usdc_mint_address = &testing_context.get_usdc_mint_address(); + let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); + + let fixture_accounts = testing_context + .fixture_accounts + .clone() + .expect("Fixture accounts not found"); + let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = super::shims::create_guardian_signatures( test_ctx, @@ -324,16 +333,26 @@ pub async fn prepare_order_response_test( ) .await; - let source_remote_token_messenger = utils::router::get_remote_token_messenger( - test_ctx, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await; + let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_ctx, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; let cctp_nonce = deposit.cctp_nonce; - println!("cctp nonce: {:?}", cctp_nonce); let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; - let fast_market_order_state = initial_offer_fixture.fast_market_order; + let fast_market_order_state = testing_engine_state + .fast_market_order() + .expect("could not find fast market order") + .fast_market_order; + let custodian_address = testing_engine_state + .custodian_address() + .expect("Custodian address not found"); // TODO: Make checks to see if fast market order sender matches cctp message sender ... let cctp_message_decoded = utils::cctp_message::craft_cctp_token_burn_message( test_ctx, @@ -343,7 +362,7 @@ pub async fn prepare_order_response_test( &message_transmitter_config_pubkey, &(&source_remote_token_messenger).into(), cctp_mint_recipient, - custodian_address, + &custodian_address, ) .await .unwrap(); @@ -361,13 +380,16 @@ pub async fn prepare_order_response_test( &fast_market_order_state, guardian_set_bump, ); + let fast_market_order_address = testing_engine_state + .fast_market_order() + .expect("could not find fast market order") + .fast_market_order_address; let cctp_message_decoded = prepare_order_response_cctp_shim_data.decode_cctp_message(); let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new( &payer_signer.pubkey(), &fixture_accounts, - &execute_order_fixture, - &initial_offer_fixture, - &initialize_fixture, + &custodian_address, + &fast_market_order_address, &from_endpoint_address, &to_endpoint_address, &usdc_mint_address, @@ -375,16 +397,15 @@ pub async fn prepare_order_response_test( &guardian_set_pubkey, &guardian_signatures_pubkey, ); - let result = super::shims_prepare_order_response::prepare_order_response_cctp_shim( - test_ctx, + super::shims_prepare_order_response::prepare_order_response_cctp_shim( + testing_context, payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, - matching_engine_program_id, + &matching_engine_program_id, + expected_error, ) - .await; - assert!(result.is_ok()); - result + .await } pub struct PrepareOrderResponseShimFixture { diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs index 3f3245eb0..33fc5da32 100644 --- a/solana/programs/matching-engine/tests/shimless/execute_order.rs +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::testing_engine::config::ExpectedError; use crate::utils::account_fixtures::FixtureAccounts; use crate::utils::auction::{AuctionAccounts, AuctionState}; @@ -14,16 +16,20 @@ use matching_engine::accounts::{ }; use matching_engine::instruction::ExecuteFastOrderCctp as ExecuteOrderShimlessInstruction; use solana_sdk::instruction::Instruction; +use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::sysvar::SysvarId; use solana_sdk::transaction::Transaction; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; -pub struct ExecuteOrderShimlessFixture {} +pub struct ExecuteOrderShimlessFixture { + pub cctp_message: Pubkey, +} pub fn create_execute_order_shimless_accounts( - testing_context: &mut TestingContext, + testing_context: &TestingContext, fixture_accounts: &FixtureAccounts, auction_accounts: &AuctionAccounts, + payer_signer: &Rc, auction_state: &AuctionState, ) -> ExecuteOrderShimlessAccounts { let active_auction_state = auction_state.get_active_auction().unwrap(); @@ -56,7 +62,7 @@ pub fn create_execute_order_shimless_accounts( core_bridge_program: wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, }; let fast_vaa = LiquidityLayerVaa { - vaa: auction_accounts.fast_vaa.unwrap(), + vaa: auction_accounts.posted_fast_vaa.unwrap(), }; let active_auction = matching_engine::accounts::ActiveAuction { auction: active_auction_address, @@ -68,7 +74,7 @@ pub fn create_execute_order_shimless_accounts( fast_vaa, active_auction, executor_token: active_auction_state.best_offer.offer_token, // TODO: Is this correct? - initial_participant: testing_context.testing_actors.owner.pubkey(), + initial_participant: active_auction_state.initial_offer.participant, initial_offer_token: active_auction_state.initial_offer.offer_token, }; let core_message = Pubkey::find_program_address( @@ -128,7 +134,7 @@ pub fn create_execute_order_shimless_accounts( ) .0; ExecuteOrderShimlessAccounts { - payer: testing_context.testing_actors.owner.pubkey(), + payer: payer_signer.pubkey(), core_message, cctp_message, to_router_endpoint, @@ -145,10 +151,11 @@ pub fn create_execute_order_shimless_accounts( } pub async fn execute_order_shimless_test( - testing_context: &mut TestingContext, + testing_context: &TestingContext, auction_accounts: &AuctionAccounts, auction_state: &AuctionState, - expected_error: Option, + payer_signer: &Rc, + expected_error: Option<&ExpectedError>, ) -> Option { crate::utils::setup::fast_forward_slots(&testing_context.test_context, 3).await; let fixture_accounts = testing_context @@ -159,6 +166,7 @@ pub async fn execute_order_shimless_test( testing_context, &fixture_accounts, auction_accounts, + payer_signer, auction_state, ); let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); @@ -169,8 +177,8 @@ pub async fn execute_order_shimless_test( }; let tx = Transaction::new_signed_with_payer( &[execute_order_ix], - Some(&testing_context.testing_actors.owner.pubkey()), - &[&testing_context.testing_actors.owner.keypair()], + Some(&payer_signer.pubkey()), + &[payer_signer], testing_context .test_context .borrow_mut() @@ -178,17 +186,14 @@ pub async fn execute_order_shimless_test( .await .unwrap(), ); - let tx_result = testing_context - .test_context - .borrow_mut() - .banks_client - .process_transaction(tx) + testing_context + .execute_and_verify_transaction(tx, expected_error) .await; if expected_error.is_none() { - assert!(tx_result.is_ok()); - Some(ExecuteOrderShimlessFixture {}) + Some(ExecuteOrderShimlessFixture { + cctp_message: execute_order_accounts.cctp_message, + }) } else { - assert!(tx_result.is_err()); None } } diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs index e0305e9d7..028a02cd3 100644 --- a/solana/programs/matching-engine/tests/shimless/make_offer.rs +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::testing_engine::config::ExpectedError; use super::super::utils; @@ -15,6 +17,7 @@ use matching_engine::instruction::{ }; use matching_engine::state::Auction; use solana_sdk::instruction::Instruction; +use solana_sdk::signature::Keypair; use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; use utils::auction::{ActiveAuctionState, AuctionAccounts, AuctionOffer, AuctionState}; @@ -26,11 +29,11 @@ pub async fn place_initial_offer_shimless( accounts: &AuctionAccounts, fast_market_order: &TestVaa, offer_price: u64, + payer_signer: &Rc, program_id: Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { let test_ctx = &testing_context.test_context; - let owner_keypair = testing_context.testing_actors.owner.keypair(); let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id, @@ -104,7 +107,7 @@ pub async fn place_initial_offer_shimless( custodian: accounts.custodian, }; let initial_offer_accounts = PlaceInitialOfferCctpAccounts { - payer: owner_keypair.pubkey(), + payer: payer_signer.pubkey(), transfer_authority, custodian, auction_config: accounts.auction_config, @@ -136,8 +139,8 @@ pub async fn place_initial_offer_shimless( let tx = Transaction::new_signed_with_payer( &[initial_offer_ix_anchor], - Some(&owner_keypair.pubkey()), - &[&owner_keypair], + Some(&payer_signer.pubkey()), + &[payer_signer], test_ctx.borrow().last_blockhash, ); @@ -152,10 +155,12 @@ pub async fn place_initial_offer_shimless( auction_custody_token_address, auction_config_address: accounts.auction_config, initial_offer: AuctionOffer { + participant: payer_signer.pubkey(), offer_token: accounts.offer_token, offer_price: initial_offer_ix.offer_price, }, best_offer: AuctionOffer { + participant: payer_signer.pubkey(), offer_token: accounts.offer_token, offer_price: initial_offer_ix.offer_price, }, @@ -171,11 +176,11 @@ pub async fn improve_offer( solver: Solver, auction_config: Pubkey, offer_price: u64, + payer_signer: &Rc, initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, ) -> Option { let test_ctx = &testing_context.test_context; - let owner_keypair = testing_context.testing_actors.owner.keypair(); let active_auction_state = initial_auction_state.get_active_auction().unwrap(); let auction_address = active_auction_state.auction_address; let auction_custody_token_address = active_auction_state.auction_custody_token_address; @@ -228,8 +233,8 @@ pub async fn improve_offer( let tx = Transaction::new_signed_with_payer( &[improve_offer_ix_anchor], - Some(&owner_keypair.pubkey()), - &[&owner_keypair], + Some(&payer_signer.pubkey()), + &[payer_signer], test_ctx.borrow().last_blockhash, ); @@ -249,6 +254,7 @@ pub async fn improve_offer( auction_config_address: auction_config, initial_offer: initial_offer.clone(), best_offer: AuctionOffer { + participant: payer_signer.pubkey(), offer_token, offer_price, }, diff --git a/solana/programs/matching-engine/tests/shimless/settle_auction.rs b/solana/programs/matching-engine/tests/shimless/settle_auction.rs index 422a13744..f6d561cb2 100644 --- a/solana/programs/matching-engine/tests/shimless/settle_auction.rs +++ b/solana/programs/matching-engine/tests/shimless/settle_auction.rs @@ -1,27 +1,29 @@ +use crate::testing_engine::config::ExpectedError; use crate::utils::auction::AuctionState; +use crate::utils::setup::TestingContext; -use super::super::shimful::*; use anchor_lang::prelude::*; use anchor_lang::InstructionData; use anchor_spl::token::spl_token; use matching_engine::accounts::SettleAuctionComplete as SettleAuctionCompleteCpiAccounts; use matching_engine::instruction::SettleAuctionComplete; -use solana_program_test::*; use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; -use std::cell::RefCell; use std::rc::Rc; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; pub async fn settle_auction_complete( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, - usdc_mint_address: &Pubkey, - prepare_order_response_shim_fixture: &shims_prepare_order_response::PrepareOrderResponseShimFixture, auction_state: &AuctionState, + prepare_order_response_address: &Pubkey, + prepared_custody_token: &Pubkey, matching_engine_program_id: &Pubkey, -) -> Result<()> { + expected_error: Option<&ExpectedError>, +) -> AuctionState { + let test_ctx = &testing_context.test_context; + let usdc_mint_address = &testing_context.get_usdc_mint_address(); let active_auction = auction_state .get_active_auction() .expect("Failed to get active auction"); @@ -32,8 +34,8 @@ pub async fn settle_auction_complete( let settle_auction_accounts = SettleAuctionCompleteCpiAccounts { beneficiary: payer_signer.pubkey(), base_fee_token: base_fee_token, - prepared_order_response: prepare_order_response_shim_fixture.prepared_order_response, - prepared_custody_token: prepare_order_response_shim_fixture.prepared_custody_token, + prepared_order_response: prepare_order_response_address.clone(), + prepared_custody_token: prepared_custody_token.clone(), auction: active_auction.auction_address, best_offer_token: active_auction.best_offer.offer_token, token_program: spl_token::ID, @@ -56,12 +58,12 @@ pub async fn settle_auction_complete( test_ctx.borrow().last_blockhash, ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(tx) - .await - .expect("Failed to settle auction"); - - Ok(()) + testing_context + .execute_and_verify_transaction(tx, expected_error) + .await; + if expected_error.is_none() { + AuctionState::Settled + } else { + auction_state.clone() + } } diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs index 9ffecb110..c66e0191b 100644 --- a/solana/programs/matching-engine/tests/testing_engine/config.rs +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -1,7 +1,10 @@ -use std::collections::HashSet; +use std::{collections::HashSet, rc::Rc}; use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; use anchor_lang::prelude::*; +use solana_sdk::signature::Keypair; + +pub type OverwriteCurrentState = Option; #[derive(Clone)] pub struct ExpectedError { @@ -41,7 +44,8 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { #[derive(Clone)] pub struct InitializeFastMarketOrderShimInstructionConfig { pub fast_market_order_id: u32, - pub close_account_refund_recipient: Pubkey, + pub close_account_refund_recipient: Option, // If none defaults to solver 0 pubkey, + pub payer_signer: Option>, // If none defaults to owner keypair pub expected_error: Option, } @@ -49,7 +53,74 @@ impl Default for InitializeFastMarketOrderShimInstructionConfig { fn default() -> Self { Self { fast_market_order_id: 0, - close_account_refund_recipient: Pubkey::new_unique(), + close_account_refund_recipient: None, + payer_signer: None, + expected_error: None, + } + } +} + +#[derive(Clone)] +pub struct PrepareOrderInstructionConfig { + pub fast_market_order_address: OverwriteCurrentState, + pub payer_signer: Option>, + pub expected_error: Option, +} + +impl Default for PrepareOrderInstructionConfig { + fn default() -> Self { + Self { + fast_market_order_address: None, + payer_signer: None, + expected_error: None, + } + } +} +#[derive(Clone)] +pub struct ExecuteOrderInstructionConfig { + pub fast_market_order_address: OverwriteCurrentState, + pub solver_index: usize, + pub payer_signer: Option>, + pub expected_error: Option, +} + +impl Default for ExecuteOrderInstructionConfig { + fn default() -> Self { + Self { + fast_market_order_address: None, + solver_index: 0, + payer_signer: None, + expected_error: None, + } + } +} + +#[derive(Clone)] +pub struct SettleAuctionInstructionConfig { + pub payer_signer: Option>, + pub expected_error: Option, +} + +impl Default for SettleAuctionInstructionConfig { + fn default() -> Self { + Self { + payer_signer: None, + expected_error: None, + } + } +} +#[derive(Clone)] +pub struct CloseFastMarketOrderShimInstructionConfig { + pub close_account_refund_recipient_keypair: Option>, // If none, will use the solver 0 keypair + pub fast_market_order_address: OverwriteCurrentState, // If none, will use the fast market order address from the current state + pub expected_error: Option, +} + +impl Default for CloseFastMarketOrderShimInstructionConfig { + fn default() -> Self { + Self { + close_account_refund_recipient_keypair: None, + fast_market_order_address: None, expected_error: None, } } @@ -58,6 +129,8 @@ impl Default for InitializeFastMarketOrderShimInstructionConfig { pub struct PlaceInitialOfferInstructionConfig { pub solver_index: usize, pub offer_price: u64, + pub payer_signer: Option>, + pub fast_market_order_address: OverwriteCurrentState, pub expected_error: Option, } @@ -66,6 +139,8 @@ impl Default for PlaceInitialOfferInstructionConfig { Self { solver_index: 0, offer_price: 1__000_000, + payer_signer: None, + fast_market_order_address: None, expected_error: None, } } @@ -74,6 +149,7 @@ impl Default for PlaceInitialOfferInstructionConfig { pub struct ImproveOfferInstructionConfig { pub solver_index: usize, pub offer_price: u64, + pub payer_signer: Option>, pub expected_error: Option, } @@ -82,6 +158,7 @@ impl Default for ImproveOfferInstructionConfig { Self { solver_index: 0, offer_price: 500_000, + payer_signer: None, expected_error: None, } } diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index e47327b12..6761cde24 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -2,10 +2,12 @@ use matching_engine::state::FastMarketOrder; use solana_sdk::transaction::VersionedTransaction; use super::{config::*, state::*}; +use crate::shimful; use crate::shimful::shims::{ create_fast_market_order_state_from_vaa_data, create_guardian_signatures, initialise_fast_market_order_fallback_instruction, }; +use crate::utils::auction::AuctionState; use crate::utils::{ auction::AuctionAccounts, router::create_all_router_endpoints_test, setup::TestingContext, }; @@ -20,12 +22,12 @@ pub enum InstructionTrigger { PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), ImproveOfferShimless(ImproveOfferInstructionConfig), - ExecuteOrderShimless, - ExecuteOrderShim, - PrepareOrderShimless, - PrepareOrderShim, - SettleAuction, - CloseFastMarketOrderShim, + ExecuteOrderShimless(ExecuteOrderInstructionConfig), + ExecuteOrderShim(ExecuteOrderInstructionConfig), + PrepareOrderShimless(PrepareOrderInstructionConfig), + PrepareOrderShim(PrepareOrderInstructionConfig), + SettleAuction(SettleAuctionInstructionConfig), + CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } pub struct TestingEngine { @@ -64,6 +66,9 @@ impl TestingEngine { self.place_initial_offer_shimless(current_state, config) .await } + InstructionTrigger::PlaceInitialOfferShim(config) => { + self.place_initial_offer_shim(current_state, config).await + } InstructionTrigger::InitializeFastMarketOrderShim(config) => { self.create_fast_market_order_account(current_state, config) .await @@ -71,7 +76,25 @@ impl TestingEngine { InstructionTrigger::ImproveOfferShimless(config) => { self.improve_offer_shimless(current_state, config).await } - _ => panic!("Not implemented yet"), // Not implemented yet + InstructionTrigger::ExecuteOrderShim(config) => { + self.execute_order_shim(current_state, config).await + } + InstructionTrigger::ExecuteOrderShimless(config) => { + self.execute_order_shimless(current_state, config).await + } + InstructionTrigger::PrepareOrderShimless(config) => { + self.prepare_order_shimless(current_state, config).await + } + InstructionTrigger::PrepareOrderShim(config) => { + self.prepare_order_shim(current_state, config).await + } + InstructionTrigger::SettleAuction(config) => { + self.settle_auction(current_state, config).await + } + InstructionTrigger::CloseFastMarketOrderShim(config) => { + self.close_fast_market_order_account(current_state, config) + .await + } } } @@ -173,14 +196,20 @@ impl TestingEngine { let fast_transfer_vaa = first_test_vaa_pair.fast_transfer_vaa.clone(); let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data( &fast_transfer_vaa.vaa_data, - config.close_account_refund_recipient, + config + .close_account_refund_recipient + .unwrap_or(self.testing_context.testing_actors.solvers[0].pubkey()), ); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = create_guardian_signatures( &self.testing_context.test_context, - &self.testing_context.testing_actors.owner.keypair(), + &payer_signer, &vaa_data, - &BaseState::CORE_BRIDGE_PROGRAM_ID, + &self.testing_context.get_wormhole_program_id(), None, ) .await; @@ -195,7 +224,7 @@ impl TestingEngine { ); let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( - &self.testing_context.testing_actors.owner.keypair(), + &payer_signer, &self.testing_context.get_matching_engine_program_id(), fast_market_order, guardian_set_pubkey, @@ -212,28 +241,65 @@ impl TestingEngine { ); let versioned_transaction = VersionedTransaction::try_from(transaction) .expect("Failed to convert transaction to versioned transaction"); - let res = self - .testing_context - .test_context - .borrow_mut() - .banks_client - .process_transaction(versioned_transaction) + self.testing_context + .execute_and_verify_transaction(versioned_transaction, config.expected_error.as_ref()) .await; if config.expected_error.is_none() { - res.expect("Failed to initialise fast market order"); TestingEngineState::FastMarketOrderAccountCreated { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), + router_endpoints: current_state.router_endpoints().cloned(), fast_market_order: FastMarketOrderAccountCreatedState { fast_market_order_address: fast_market_order_account, fast_market_order_bump: fast_market_order_bump, + fast_market_order: fast_market_order, + }, + guardian_set_state: GuardianSetState { + guardian_set_address: guardian_set_pubkey, + guardian_signatures_address: guardian_signatures_pubkey, }, } } else { current_state.clone() } } + + async fn close_fast_market_order_account( + &self, + current_state: &TestingEngineState, + config: &CloseFastMarketOrderShimInstructionConfig, + ) -> TestingEngineState { + // Get the fast market order account from the current state. If it is not present, panic + let fast_market_order_account = config.fast_market_order_address.unwrap_or( + current_state + .fast_market_order() + .expect("Fast market order account not found") + .fast_market_order_address, + ); + let close_account_refund_recipient = config + .close_account_refund_recipient_keypair + .clone() + .unwrap_or(self.testing_context.testing_actors.solvers[0].keypair()); + + shimful::shims::close_fast_market_order_fallback( + &self.testing_context, + &close_account_refund_recipient, + &self.testing_context.get_matching_engine_program_id(), + &fast_market_order_account, + config.expected_error.as_ref(), + ) + .await; + + TestingEngineState::FastMarketOrderClosed { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().cloned(), + auction_state: current_state.auction_state().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: current_state.order_prepared().cloned(), + auction_accounts: current_state.auction_accounts().cloned(), + } + } async fn place_initial_offer_shimless( &self, current_state: &TestingEngineState, @@ -243,6 +309,10 @@ impl TestingEngine { current_state.router_endpoints().is_some(), "Router endpoints are not created" ); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); let solver = self .testing_context .testing_actors @@ -282,6 +352,7 @@ impl TestingEngine { &auction_accounts, fast_vaa, config.offer_price, + &payer_signer, self.testing_context.get_matching_engine_program_id(), expected_error, ) @@ -298,6 +369,7 @@ impl TestingEngine { router_endpoints: current_state.router_endpoints().unwrap().clone(), fast_market_order: current_state.fast_market_order().cloned(), auction_state, + auction_accounts, }; } current_state.clone() @@ -319,12 +391,17 @@ impl TestingEngine { let auction_config_address = current_state .auction_config_address() .expect("Auction config address not found"); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, self.testing_context.get_matching_engine_program_id(), solver.clone(), auction_config_address, offer_price, + &payer_signer, current_state.auction_state(), expected_error, ) @@ -337,8 +414,285 @@ impl TestingEngine { router_endpoints: current_state.router_endpoints().unwrap().clone(), fast_market_order: current_state.fast_market_order().cloned(), auction_state, + auction_accounts: current_state.auction_accounts().cloned(), }; } current_state.clone() } + + async fn place_initial_offer_shim( + &self, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + ) -> TestingEngineState { + let fast_market_order_address = config.fast_market_order_address.unwrap_or( + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address, + ); + let router_endpoints = current_state + .router_endpoints() + .expect("Router endpoints are not created"); + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); + let auction_accounts = AuctionAccounts::new( + None, + solver.clone(), + auction_config_address, + &router_endpoints.endpoints, + custodian_address, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, + ); + let fast_vaa_data = current_state + .get_first_test_vaa_pair() + .fast_transfer_vaa + .get_vaa_data(); + let place_initial_offer_shim_fixture = shimful::shims::place_initial_offer_fallback( + &self.testing_context, + &payer_signer, + &fast_vaa_data, + solver, + &fast_market_order_address, + &auction_accounts, + config.offer_price, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: initial_offer_placed_state.auction_state, + auction_accounts, + }; + } + current_state.clone() + } + + async fn execute_order_shim( + &self, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, + ) -> TestingEngineState { + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + + // TODO: Change to get auction accounts from current state + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let fast_market_order_address = config.fast_market_order_address.unwrap_or( + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address, + ); + let active_auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction not found"); + let result = shimful::shims_execute_order::execute_order_fallback_test( + &self.testing_context, + &auction_accounts, + &fast_market_order_address, + &active_auction_state, + solver, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let order_executed_fallback_fixture = result.unwrap(); + let order_executed_state = OrderExecutedState { + cctp_message: order_executed_fallback_fixture.cctp_message, + post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), + post_message_message: Some(order_executed_fallback_fixture.post_message_message), + }; + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } + + async fn execute_order_shimless( + &self, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, + ) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let router_endpoints = current_state + .router_endpoints() + .expect("Router endpoints are not created"); + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); + let auction_accounts = AuctionAccounts::new( + Some( + current_state + .get_first_test_vaa_pair() + .fast_transfer_vaa + .get_vaa_pubkey(), + ), + solver.clone(), + auction_config_address, + &router_endpoints.endpoints, + custodian_address, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, + ); + let result = shimless::execute_order::execute_order_shimless_test( + &self.testing_context, + &auction_accounts, + current_state.auction_state(), + &payer_signer, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let execute_order_fixture = result.unwrap(); + let order_executed_state = OrderExecutedState { + cctp_message: execute_order_fixture.cctp_message, + post_message_sequence: None, + post_message_message: None, + }; + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } + + async fn prepare_order_shim( + &self, + current_state: &TestingEngineState, + config: &PrepareOrderInstructionConfig, + ) -> TestingEngineState { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + + let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); + let deposit_vaa_data = deposit_vaa.get_vaa_data(); + let deposit = deposit_vaa + .payload_deserialized + .clone() + .unwrap() + .get_deposit() + .unwrap(); + + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + + let result = shimful::shims_prepare_order_response::prepare_order_response_test( + &self.testing_context, + &payer_signer, + &deposit_vaa_data, + current_state, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, + &deposit, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let prepare_order_response_fixture = result.unwrap(); + let order_prepared_state = OrderPreparedState { + prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + }; + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } + + async fn prepare_order_shimless( + &self, + _current_state: &TestingEngineState, + _config: &PrepareOrderInstructionConfig, + ) -> TestingEngineState { + panic!("Not implemented yet"); + } + + async fn settle_auction( + &self, + current_state: &TestingEngineState, + config: &SettleAuctionInstructionConfig, + ) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let order_prepared_state = current_state + .order_prepared() + .expect("Order prepared not found"); + let prepared_custody_token = order_prepared_state.prepared_custody_token; + let prepared_order_response = order_prepared_state.prepared_order_address; + let auction_state = shimless::settle_auction::settle_auction_complete( + &self.testing_context, + &payer_signer, + current_state.auction_state(), + &prepared_order_response, + &prepared_custody_token, + &self.testing_context.get_matching_engine_program_id(), + config.expected_error.as_ref(), + ) + .await; + match auction_state { + AuctionState::Settled => TestingEngineState::AuctionSettled { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + auction_state: current_state.auction_state().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: order_prepared_state.clone(), + auction_accounts: current_state.auction_accounts().cloned(), + }, + _ => current_state.clone(), + } + } } diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/programs/matching-engine/tests/testing_engine/state.rs index a737a2fc8..b588da956 100644 --- a/solana/programs/matching-engine/tests/testing_engine/state.rs +++ b/solana/programs/matching-engine/tests/testing_engine/state.rs @@ -1,13 +1,12 @@ use crate::utils::{ account_fixtures::FixtureAccounts, - auction::AuctionState, + auction::{AuctionAccounts, AuctionState}, router::TestRouterEndpoints, setup::TransferDirection, vaa::{TestVaaPair, TestVaaPairs}, }; use anchor_lang::prelude::*; -use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; -use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; +use matching_engine::state::FastMarketOrder; // Base state containing common data #[derive(Clone)] @@ -17,15 +16,6 @@ pub struct BaseState { pub transfer_direction: TransferDirection, } -impl BaseState { - pub const CCTP_MINT_RECIPIENT: Pubkey = CCTP_MINT_RECIPIENT; - pub const CORE_BRIDGE_PROGRAM_ID: Pubkey = CORE_BRIDGE_PROGRAM_ID; - - pub fn get_matching_engine_program_id(&self) -> Pubkey { - PROGRAM_ID - } -} - // Each state contains its specific data #[derive(Clone)] pub struct InitializedState { @@ -42,6 +32,7 @@ pub struct RouterEndpointsState { pub struct FastMarketOrderAccountCreatedState { pub fast_market_order_address: Pubkey, pub fast_market_order_bump: u8, + pub fast_market_order: FastMarketOrder, } #[derive(Clone)] @@ -56,12 +47,21 @@ pub struct OfferImprovedState { #[derive(Clone)] pub struct OrderExecutedState { - pub auction_state: AuctionState, + pub cctp_message: Pubkey, + pub post_message_sequence: Option, // Only set if shimful execution + pub post_message_message: Option, // Only set if shimful execution } #[derive(Clone)] pub struct OrderPreparedState { pub prepared_order_address: Pubkey, + pub prepared_custody_token: Pubkey, +} + +#[derive(Clone)] +pub struct GuardianSetState { + pub guardian_set_address: Pubkey, + pub guardian_signatures_address: Pubkey, } // The main state enum that reflects all possible instruction states @@ -80,8 +80,9 @@ pub enum TestingEngineState { FastMarketOrderAccountCreated { base: BaseState, initialized: InitializedState, - router_endpoints: RouterEndpointsState, + router_endpoints: Option, fast_market_order: FastMarketOrderAccountCreatedState, + guardian_set_state: GuardianSetState, }, InitialOfferPlaced { base: BaseState, @@ -89,6 +90,7 @@ pub enum TestingEngineState { router_endpoints: RouterEndpointsState, fast_market_order: Option, auction_state: AuctionState, + auction_accounts: AuctionAccounts, }, OfferImproved { base: BaseState, @@ -96,6 +98,7 @@ pub enum TestingEngineState { router_endpoints: RouterEndpointsState, fast_market_order: Option, auction_state: AuctionState, + auction_accounts: Option, }, OrderExecuted { base: BaseState, @@ -103,6 +106,8 @@ pub enum TestingEngineState { router_endpoints: RouterEndpointsState, fast_market_order: Option, auction_state: AuctionState, + order_executed: OrderExecutedState, + auction_accounts: AuctionAccounts, }, OrderPrepared { base: BaseState, @@ -111,6 +116,7 @@ pub enum TestingEngineState { fast_market_order: Option, auction_state: AuctionState, order_prepared: OrderPreparedState, + auction_accounts: AuctionAccounts, }, AuctionSettled { base: BaseState, @@ -119,14 +125,16 @@ pub enum TestingEngineState { auction_state: AuctionState, fast_market_order: Option, order_prepared: OrderPreparedState, + auction_accounts: Option, }, FastMarketOrderClosed { base: BaseState, initialized: InitializedState, - router_endpoints: RouterEndpointsState, + router_endpoints: Option, auction_state: AuctionState, fast_market_order: Option, order_prepared: Option, + auction_accounts: Option, }, } @@ -173,7 +181,7 @@ impl TestingEngineState { } => Some(router_endpoints), Self::FastMarketOrderAccountCreated { router_endpoints, .. - } => Some(router_endpoints), + } => router_endpoints.as_ref(), Self::InitialOfferPlaced { router_endpoints, .. } => Some(router_endpoints), @@ -191,7 +199,7 @@ impl TestingEngineState { } => Some(router_endpoints), Self::FastMarketOrderClosed { router_endpoints, .. - } => Some(router_endpoints), + } => router_endpoints.as_ref(), } } @@ -229,8 +237,32 @@ impl TestingEngineState { } } + pub fn auction_accounts(&self) -> Option<&AuctionAccounts> { + match self { + Self::InitialOfferPlaced { + auction_accounts, .. + } => Some(auction_accounts), + Self::OfferImproved { + auction_accounts, .. + } => auction_accounts.as_ref(), + Self::OrderExecuted { + auction_accounts, .. + } => Some(auction_accounts), + Self::OrderPrepared { + auction_accounts, .. + } => Some(auction_accounts), + Self::AuctionSettled { + auction_accounts, .. + } => auction_accounts.as_ref(), + Self::FastMarketOrderClosed { + auction_accounts, .. + } => auction_accounts.as_ref(), + _ => None, + } + } + // Prepared order accessor - pub fn prepared_order(&self) -> Option<&OrderPreparedState> { + pub fn order_prepared(&self) -> Option<&OrderPreparedState> { match self { Self::OrderPrepared { order_prepared, .. } => Some(order_prepared), Self::AuctionSettled { order_prepared, .. } => Some(order_prepared), diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index 579159b6b..c7f1e11ff 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,18 +1,16 @@ use anchor_lang::prelude::*; -use super::super::shimless; use super::router::TestRouterEndpoints; use super::setup::{Solver, TransferDirection}; use super::Chain; use matching_engine::state::{Auction, AuctionInfo}; use solana_program_test::ProgramTestContext; use std::cell::RefCell; -use std::collections::HashSet; use std::rc::Rc; #[derive(Clone)] pub struct AuctionAccounts { - pub fast_vaa: Option, + pub posted_fast_vaa: Option, pub offer_token: Pubkey, pub solver: Solver, pub auction_config: Pubkey, @@ -25,6 +23,7 @@ pub struct AuctionAccounts { #[derive(Clone)] pub enum AuctionState { Active(ActiveAuctionState), + Settled, Inactive, } @@ -33,6 +32,7 @@ impl AuctionState { match self { AuctionState::Active(auction) => Some(auction), AuctionState::Inactive => None, + AuctionState::Settled => None, } } } @@ -47,13 +47,14 @@ pub struct ActiveAuctionState { #[derive(Clone)] pub struct AuctionOffer { + pub participant: Pubkey, pub offer_token: Pubkey, pub offer_price: u64, } impl AuctionAccounts { pub fn new( - fast_vaa: Option, + posted_fast_vaa: Option, solver: Solver, auction_config: Pubkey, router_endpoints: &TestRouterEndpoints, @@ -72,7 +73,7 @@ impl AuctionAccounts { ), }; Self { - fast_vaa, + posted_fast_vaa, offer_token: solver.token_account_address().unwrap(), solver, auction_config, @@ -82,35 +83,6 @@ impl AuctionAccounts { usdc_mint, } } - - pub async fn create_auction_accounts( - testing_context: &mut super::setup::TestingContext, - initialize_fixture: &shimless::initialize::InitializeFixture, - transfer_direction: TransferDirection, - fast_vaa_pubkey: Option, - ) -> Self { - let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_config_address = initialize_fixture.get_auction_config_address(); - let router_endpoints = super::router::create_all_router_endpoints_test( - &testing_context, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair(), - HashSet::from([Chain::Ethereum, Chain::Arbitrum, Chain::Solana]), - ) - .await; - - let solver = testing_context.testing_actors.solvers[0].clone(); - Self::new( - fast_vaa_pubkey, // Fast VAA pubkey - solver.clone(), // Solver - auction_config_address.clone(), // Auction config pubkey - &router_endpoints, // Router endpoints - initialize_fixture.get_custodian_address(), // Custodian pubkey - usdc_mint_address, // USDC mint pubkey - transfer_direction, - ) - } } impl ActiveAuctionState { diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 22feaff46..1c4961aa2 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -202,6 +202,10 @@ impl TestVaa { pub fn get_vaa_pubkey(&self) -> Pubkey { self.vaa_pubkey.clone() } + + pub fn get_vaa_data(&self) -> &PostedVaaData { + &self.vaa_data + } } #[derive(Clone)] From bc633fd3a4a8b3b48c2990166332bb51c8489902 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 11:24:36 +0000 Subject: [PATCH 026/112] working tests --- solana/programs/matching-engine/Cargo.toml | 4 + .../processor/prepare_order_response.rs | 80 ++++-- solana/programs/matching-engine/src/lib.rs | 7 + .../tests/integration_tests.rs | 102 ++++++++ .../tests/shimful/shims_execute_order.rs | 1 + .../shimful/shims_prepare_order_response.rs | 107 +++----- .../tests/shimless/execute_order.rs | 1 + .../matching-engine/tests/shimless/mod.rs | 1 + .../tests/shimless/prepare_order_response.rs | 227 +++++++++++++++++ .../tests/testing_engine/config.rs | 4 + .../tests/testing_engine/engine.rs | 100 +++++++- .../tests/testing_engine/state.rs | 6 + .../matching-engine/tests/utils/auction.rs | 35 +-- .../tests/utils/cctp_message.rs | 45 +++- .../matching-engine/tests/utils/mod.rs | 3 +- .../matching-engine/tests/utils/setup.rs | 26 ++ .../tests/utils/testing_engine.rs | 237 ------------------ .../tests/utils/testing_engine_configs.rs | 82 ------ .../matching-engine/tests/utils/tracing.rs | 97 +++++++ .../matching-engine/tests/utils/vaa.rs | 4 +- 20 files changed, 728 insertions(+), 441 deletions(-) create mode 100644 solana/programs/matching-engine/tests/shimless/prepare_order_response.rs delete mode 100644 solana/programs/matching-engine/tests/utils/testing_engine.rs delete mode 100644 solana/programs/matching-engine/tests/utils/testing_engine_configs.rs create mode 100644 solana/programs/matching-engine/tests/utils/tracing.rs diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 3bde7f295..923bc3465 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -57,6 +57,10 @@ bs58 = "0.5.0" serde = { version = "1.0.212", features = ["derive"] } secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context", "recovery"] } num-traits = "0.2.16" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-log = "0.2.0" +once_cell = "1.8" wormhole-svm-shim.workspace = true wormhole-svm-definitions.workspace = true diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index c2099208f..306ffb0e5 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -12,9 +12,11 @@ use crate::state::{ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use common::messages::raw::LiquidityLayerDepositMessage; +use common::messages::raw::LiquidityLayerMessage; use common::messages::raw::SlowOrderResponse; use common::wormhole_cctp_solana::cctp::message_transmitter_program; use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; +use common::wormhole_cctp_solana::utils::CctpMessage; use solana_program::instruction::Instruction; use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; @@ -225,6 +227,29 @@ pub fn prepare_order_response_cctp_shim( let system_program = &accounts[27]; let receive_message_args = data.to_receive_message_args(); let finalized_vaa_message = data.finalized_vaa_message; + + let deposit_option = LiquidityLayerMessage::parse(&finalized_vaa_message.vaa_payload) + .map_err(|_| MatchingEngineError::InvalidDeposit)?; + let deposit = deposit_option + .deposit() + .ok_or(MatchingEngineError::InvalidDepositPayloadId)?; + let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) + .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; + require_eq!( + cctp_message.source_domain(), + deposit.source_cctp_domain(), + MatchingEngineError::InvalidCctpMessage + ); + require_eq!( + cctp_message.destination_domain(), + deposit.destination_cctp_domain(), + MatchingEngineError::InvalidCctpMessage + ); + require_eq!( + cctp_message.nonce(), + deposit.cctp_nonce(), + MatchingEngineError::InvalidCctpMessage + ); // Load accounts let fast_market_order_account_data = fast_market_order.data.borrow(); let fast_market_order_zero_copy = @@ -295,28 +320,27 @@ pub fn prepare_order_response_cctp_shim( Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); // Check custodian account - require!(custodian.owner == program_id, ErrorCode::ConstraintOwner); + require_eq!(custodian.owner, program_id, ErrorCode::ConstraintOwner); require!(!checked_custodian.paused, MatchingEngineError::Paused); // Check usdc mint - require!( - usdc.key() == common::USDC_MINT, + require_eq!( + usdc.key(), + common::USDC_MINT, MatchingEngineError::InvalidMint ); // Check from_endpoint owner - require!( - from_endpoint.owner == program_id, - ErrorCode::ConstraintOwner - ); + require_eq!(from_endpoint.owner, program_id, ErrorCode::ConstraintOwner); // Check to_endpoint owner - require!(to_endpoint.owner == program_id, ErrorCode::ConstraintOwner); + require_eq!(to_endpoint.owner, program_id, ErrorCode::ConstraintOwner); // Check that the from and to endpoints are different - require!( - from_endpoint_account.chain != to_endpoint_account.chain, + require_neq!( + from_endpoint_account.chain, + to_endpoint_account.chain, MatchingEngineError::SameEndpoint ); @@ -339,39 +363,47 @@ pub fn prepare_order_response_cctp_shim( ); // Check that to endpoint chain is equal to the fast_market_order target_chain - require!( - to_endpoint_account.chain == fast_market_order_zero_copy.target_chain, + require_eq!( + to_endpoint_account.chain, + fast_market_order_zero_copy.target_chain, MatchingEngineError::InvalidTargetRouter ); - require!( - prepared_order_response_pda == prepared_order_response.key(), + require_eq!( + prepared_order_response_pda, + prepared_order_response.key(), MatchingEngineError::InvalidPda ); - require!( - prepared_custody_token_pda == prepared_custody_token.key(), + require_eq!( + prepared_custody_token_pda, + prepared_custody_token.key(), MatchingEngineError::InvalidPda ); // Check the base token fee key is not equal to the prepared custody token key - require!( - base_fee_token.key() != prepared_custody_token.key(), + // TODO: Check that base fee token is actually a token account + require_neq!( + base_fee_token.key(), + prepared_custody_token.key(), MatchingEngineError::InvalidBaseFeeToken ); - require!( - token_program.key() == spl_token::ID, + require_eq!( + token_program.key(), + spl_token::ID, MatchingEngineError::InvalidProgram ); - require!( - _verify_shim_program.key() == wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + require_eq!( + _verify_shim_program.key(), + wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, MatchingEngineError::InvalidProgram ); - require!( - system_program.key() == solana_program::system_program::ID, + require_eq!( + system_program.key(), + solana_program::system_program::ID, MatchingEngineError::InvalidProgram ); diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 93d632ae9..e4131033a 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -2,12 +2,14 @@ #![allow(clippy::result_large_err)] mod composite; +use composite::*; pub mod error; mod events; mod processor; +pub use processor::CctpMessageArgs; pub use processor::InitializeArgs; pub use processor::VaaMessage; use processor::*; @@ -485,6 +487,11 @@ pub mod matching_engine { err!(ErrorCode::Deprecated) } + /// UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol. + pub fn get_cctp_mint_recipient(_ctx: Context) -> Result<()> { + err!(ErrorCode::InstructionMissing) + } + /// Non anchor function for placing an initial offer using the VAA shim. pub fn fallback_process_instruction( program_id: &Pubkey, diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index 2f9d5cbdf..b15764772 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -485,6 +485,108 @@ pub async fn test_prepare_order_shim_fallback() { testing_engine.execute(instruction_triggers).await; } +// Prepare order response from ethereum to arbitrum (shimless) +#[tokio::test] +pub async fn test_prepare_order_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_prepare_order_response_shimful_blocks_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { + // expected_log_message: Some("Already prepared".to_string()), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_prepare_order_response_shimless_blocks_shimful() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + #[tokio::test] pub async fn test_settle_auction_complete() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index d6b637375..aea079bcd 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -54,6 +54,7 @@ impl ExecuteOrderFallbackAccounts { TransferDirection::FromArbitrumToEthereum => { fixture_accounts.ethereum_remote_token_messenger } + _ => panic!("Unsupported transfer direction"), }; Self { diff --git a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs index 04187c6ed..6e537c8b0 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs @@ -5,7 +5,6 @@ use crate::utils::setup::{TestingContext, TransferDirection}; use super::super::utils; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use common::messages::raw::LiquidityLayerDepositMessage; use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; @@ -22,6 +21,7 @@ use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; use std::rc::Rc; use utils::account_fixtures::FixtureAccounts; +use utils::cctp_message::{CctpMessageDecoded, UsedNonces}; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; pub struct PrepareOrderResponseShimAccountsFixture { @@ -50,29 +50,6 @@ pub struct PrepareOrderResponseShimAccountsFixture { pub guardian_set_signatures: Pubkey, } -struct UsedNonces; - -impl UsedNonces { - pub const MAX_NONCES: u64 = 6400; - pub fn address(remote_domain: u32, nonce: u64) -> (Pubkey, u8) { - let first_nonce = if nonce == 0 { - 0 - } else { - (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1 - }; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket - let remote_domain_converted = remote_domain.to_string(); - let first_nonce_converted = first_nonce.to_string(); - Pubkey::find_program_address( - &[ - b"used_nonces", - remote_domain_converted.as_bytes(), - first_nonce_converted.as_bytes(), - ], - &MESSAGE_TRANSMITTER_PROGRAM_ID, - ) - } -} - impl PrepareOrderResponseShimAccountsFixture { pub fn new( signer: &Pubkey, @@ -85,6 +62,7 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_message_decoded: &CctpMessageDecoded, guardian_set: &Pubkey, guardian_set_signatures: &Pubkey, + transfer_direction: &TransferDirection, ) -> Self { let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID) @@ -97,7 +75,7 @@ impl PrepareOrderResponseShimAccountsFixture { &MESSAGE_TRANSMITTER_PROGRAM_ID, ) .0; - let token_messenger_minter_event_authority = &Pubkey::find_program_address( + let token_messenger_minter_event_authority = Pubkey::find_program_address( &[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID, ) @@ -106,41 +84,43 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_message_decoded.source_domain, cctp_message_decoded.nonce, ); + let cctp_remote_token_messenger = match transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + fixture_accounts.ethereum_remote_token_messenger + } + TransferDirection::FromArbitrumToEthereum => { + fixture_accounts.arbitrum_remote_token_messenger + } + _ => panic!("Unsupported transfer direction"), + }; Self { - signer: signer.clone(), - custodian: custodian_address.clone(), - fast_market_order: fast_market_order_address.clone(), - from_endpoint: from_router_endpoint.clone(), - to_endpoint: to_router_endpoint.clone(), - base_fee_token: usdc_mint_address.clone(), // Change this to the solver's address? - usdc: usdc_mint_address.clone(), + signer: *signer, + custodian: *custodian_address, + fast_market_order: *fast_market_order_address, + from_endpoint: *from_router_endpoint, + to_endpoint: *to_router_endpoint, + base_fee_token: *usdc_mint_address, // Change this to the solver's address? + usdc: *usdc_mint_address, cctp_mint_recipient: CCTP_MINT_RECIPIENT, - cctp_message_transmitter_authority: cctp_message_transmitter_authority.clone(), - cctp_message_transmitter_config: fixture_accounts.message_transmitter_config.clone(), - cctp_used_nonces: cctp_used_nonces_pda.clone(), - cctp_message_transmitter_event_authority: cctp_message_transmitter_event_authority - .clone(), - cctp_token_messenger: fixture_accounts.token_messenger.clone(), - cctp_remote_token_messenger: fixture_accounts.ethereum_remote_token_messenger.clone(), - cctp_token_minter: fixture_accounts.token_minter.clone(), - cctp_local_token: fixture_accounts.usdc_local_token.clone(), - cctp_token_pair: fixture_accounts.usdc_token_pair.clone(), - cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), + cctp_message_transmitter_authority, + cctp_message_transmitter_config: fixture_accounts.message_transmitter_config, + cctp_used_nonces: cctp_used_nonces_pda, + cctp_message_transmitter_event_authority, + cctp_token_messenger: fixture_accounts.token_messenger, + cctp_remote_token_messenger, + cctp_token_minter: fixture_accounts.token_minter, + cctp_local_token: fixture_accounts.usdc_local_token, + cctp_token_pair: fixture_accounts.usdc_token_pair, + cctp_token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token, cctp_token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, cctp_message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, - cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority - .clone(), - guardian_set: guardian_set.clone(), - guardian_set_signatures: guardian_set_signatures.clone(), + cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority, + guardian_set: *guardian_set, + guardian_set_signatures: *guardian_set_signatures, } } } -pub struct CctpMessageDecoded { - pub nonce: u64, - pub source_domain: u32, -} - pub struct PrepareOrderResponseShimDataFixture { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, @@ -253,7 +233,6 @@ pub async fn prepare_order_response_cctp_shim( deposit_payload: data.deposit_payload, guardian_set_bump: data.guardian_set_bump, }; - let data = PrepareOrderResponseCctpShimData { encoded_cctp_message: data.encoded_cctp_message, cctp_attestation: data.cctp_attestation, @@ -291,17 +270,6 @@ pub async fn prepare_order_response_cctp_shim( } } -pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { - // TODO: Fix this - let payload = deposit.payload.clone(); - let liquidity_layer_message = LiquidityLayerDepositMessage::parse(&payload).unwrap(); - let slow_order_response = liquidity_layer_message - .slow_order_response() - .expect("Failed to get slow order response"); - let base_fee = slow_order_response.base_fee(); - base_fee -} - pub async fn prepare_order_response_test( testing_context: &TestingContext, payer_signer: &Rc, @@ -354,7 +322,7 @@ pub async fn prepare_order_response_test( .custodian_address() .expect("Custodian address not found"); // TODO: Make checks to see if fast market order sender matches cctp message sender ... - let cctp_message_decoded = utils::cctp_message::craft_cctp_token_burn_message( + let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( test_ctx, source_remote_token_messenger.domain, cctp_nonce, @@ -366,14 +334,14 @@ pub async fn prepare_order_response_test( ) .await .unwrap(); - cctp_message_decoded + cctp_token_burn_message .verify_cctp_message(&fast_market_order_state) .unwrap(); - let deposit_base_fee = super::shims_prepare_order_response::get_deposit_base_fee(&deposit); + let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new( - cctp_message_decoded.encoded_cctp_burn_message, - cctp_message_decoded.cctp_attestation, + cctp_token_burn_message.encoded_cctp_burn_message, + cctp_token_burn_message.cctp_attestation, &deposit_vaa_data, &deposit, deposit_base_fee, @@ -396,6 +364,7 @@ pub async fn prepare_order_response_test( &cctp_message_decoded, &guardian_set_pubkey, &guardian_signatures_pubkey, + &testing_context.testing_state.transfer_direction, ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( testing_context, diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs index 33fc5da32..6ddf1f426 100644 --- a/solana/programs/matching-engine/tests/shimless/execute_order.rs +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -112,6 +112,7 @@ pub fn create_execute_order_shimless_accounts( TransferDirection::FromArbitrumToEthereum => { fixture_accounts.ethereum_remote_token_messenger } + _ => panic!("Unsupported transfer direction"), }; let token_minter = Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs index 17104af95..9da720703 100644 --- a/solana/programs/matching-engine/tests/shimless/mod.rs +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -1,4 +1,5 @@ pub mod execute_order; pub mod initialize; pub mod make_offer; +pub mod prepare_order_response; pub mod settle_auction; diff --git a/solana/programs/matching-engine/tests/shimless/prepare_order_response.rs b/solana/programs/matching-engine/tests/shimless/prepare_order_response.rs new file mode 100644 index 000000000..b5f7032df --- /dev/null +++ b/solana/programs/matching-engine/tests/shimless/prepare_order_response.rs @@ -0,0 +1,227 @@ +use crate::testing_engine::state::TestingEngineState; +use crate::utils; +use crate::utils::cctp_message::UsedNonces; +use crate::utils::setup::TestingContext; +use crate::{testing_engine::config::ExpectedError, utils::setup::TransferDirection}; +use anchor_lang::InstructionData; +use anchor_lang::{prelude::*, system_program}; +use anchor_spl::token::spl_token; +use common::wormhole_cctp_solana::cctp::{ + MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, +}; +use matching_engine::accounts::{ + CctpMintRecipientMut, CctpReceiveMessage, CheckedCustodian, FastOrderPath, LiquidityLayerVaa, + LiveRouterEndpoint, LiveRouterPath, + PrepareOrderResponseCctp as PrepareOrderResponseCctpAccounts, Usdc, +}; +use matching_engine::instruction::PrepareOrderResponseCctp as PrepareOrderResponseCctpIx; +use matching_engine::state::PreparedOrderResponse; +use matching_engine::CctpMessageArgs; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::{Keypair, Signer}; +use solana_sdk::transaction::Transaction; +use std::rc::Rc; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +pub struct PrepareOrderResponseFixture { + pub prepared_order_response: Pubkey, + pub prepared_custody_token: Pubkey, +} +pub async fn prepare_order_response( + testing_context: &TestingContext, + payer_signer: &Rc, + testing_engine_state: &TestingEngineState, + to_endpoint_address: &Pubkey, + from_endpoint_address: &Pubkey, + base_fee_token_address: &Pubkey, + expected_error: Option<&ExpectedError>, + expected_log_message: Option<&String>, +) -> Option { + let test_ctx = &testing_context.test_context; + let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); + let usdc_mint_address = &testing_context.get_usdc_mint_address(); + let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); + let fixture_accounts = testing_context + .fixture_accounts + .clone() + .expect("Fixture accounts not found"); + + let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_ctx, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; + + let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; + let custodian_address = testing_engine_state + .custodian_address() + .expect("Custodian address not found"); + let first_vaa_pair = testing_engine_state.get_first_test_vaa_pair(); + let posted_fast_transfer_vaa = first_vaa_pair.clone().fast_transfer_vaa; + let posted_fast_transfer_vaa_address = posted_fast_transfer_vaa.vaa_pubkey; + let deposit = first_vaa_pair + .deposit_vaa + .clone() + .payload_deserialized + .unwrap() + .get_deposit() + .unwrap(); + let cctp_nonce = deposit.cctp_nonce; + // TODO: Make checks to see if fast market order sender matches cctp message sender ... + let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( + test_ctx, + source_remote_token_messenger.domain, + cctp_nonce, + deposit.amount, + &message_transmitter_config_pubkey, + &(&source_remote_token_messenger).into(), + cctp_mint_recipient, + &custodian_address, + ) + .await + .unwrap(); + let checked_custodian = CheckedCustodian { + custodian: custodian_address, + }; + let fast_transfer_liquidity_layer_vaa = LiquidityLayerVaa { + vaa: posted_fast_transfer_vaa_address, + }; + let fast_order_path = FastOrderPath { + fast_vaa: fast_transfer_liquidity_layer_vaa, + path: LiveRouterPath { + to_endpoint: LiveRouterEndpoint { + endpoint: to_endpoint_address.clone(), + }, + from_endpoint: LiveRouterEndpoint { + endpoint: from_endpoint_address.clone(), + }, + }, + }; + let finalized_vaa = LiquidityLayerVaa { + vaa: first_vaa_pair.deposit_vaa.vaa_pubkey, + }; + let fast_transfer_digest = posted_fast_transfer_vaa.get_vaa_data().digest(); + let prepared_order_response_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + fast_transfer_digest.as_ref(), + ]; + let (prepared_order_response_pda, _prepared_order_response_bump) = + Pubkey::find_program_address(&prepared_order_response_seeds, matching_engine_program_id); + let prepared_custody_token_seeds = [ + matching_engine::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_pda.as_ref(), + ]; + let (prepared_custody_token_pda, _prepared_custody_token_bump) = + Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); + + let usdc = Usdc { + mint: usdc_mint_address.clone(), + }; + let (used_nonces_pda, _used_nonces_bump) = + UsedNonces::address(source_remote_token_messenger.domain, cctp_nonce); + let cctp_message_transmitter_authority = Pubkey::find_program_address( + &[ + b"message_transmitter_authority", + &TOKEN_MESSENGER_MINTER_PROGRAM_ID.as_ref(), + ], + &MESSAGE_TRANSMITTER_PROGRAM_ID, + ) + .0; + let token_messenger_minter_event_authority = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + + let cctp_mint_recipient = CctpMintRecipientMut { + mint_recipient: cctp_mint_recipient.clone(), + }; + let cctp_message_transmitter_event_authority = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let cctp_remote_token_messenger = match testing_context.testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + fixture_accounts.ethereum_remote_token_messenger + } + TransferDirection::FromArbitrumToEthereum => { + fixture_accounts.arbitrum_remote_token_messenger + } + _ => panic!("Unsupported transfer direction"), + }; + let cctp = CctpReceiveMessage { + mint_recipient: cctp_mint_recipient, + message_transmitter_authority: cctp_message_transmitter_authority.clone(), + message_transmitter_config: message_transmitter_config_pubkey.clone(), + used_nonces: used_nonces_pda, + message_transmitter_event_authority: cctp_message_transmitter_event_authority, + token_messenger: fixture_accounts.token_messenger.clone(), + remote_token_messenger: cctp_remote_token_messenger, + token_minter: fixture_accounts.token_minter.clone(), + local_token: fixture_accounts.usdc_local_token.clone(), + token_pair: fixture_accounts.usdc_token_pair.clone(), + token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), + token_messenger_minter_event_authority: token_messenger_minter_event_authority, + token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, + message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, + }; + let prepared_order_response_accounts = PrepareOrderResponseCctpAccounts { + payer: payer_signer.pubkey(), + custodian: checked_custodian, + fast_order_path: fast_order_path, + finalized_vaa: finalized_vaa, + prepared_order_response: prepared_order_response_pda, + prepared_custody_token: prepared_custody_token_pda, + base_fee_token: *base_fee_token_address, + usdc: usdc, + cctp: cctp, + token_program: spl_token::ID, + system_program: system_program::ID, + }; + + let prepare_order_response_ix_data = PrepareOrderResponseCctpIx { + args: CctpMessageArgs { + encoded_cctp_message: cctp_token_burn_message.encoded_cctp_burn_message, + cctp_attestation: cctp_token_burn_message.cctp_attestation, + }, + } + .data(); + + let instruction = Instruction { + program_id: matching_engine_program_id.clone(), + accounts: prepared_order_response_accounts.to_account_metas(None), + data: prepare_order_response_ix_data, + }; + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer_signer.pubkey()), + &[payer_signer], + test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"), + ); + if let Some(expected_log_message) = expected_log_message { + assert!( + expected_error.is_none(), + "Expected error is not allowed when expected log message is provided" + ); + testing_context + .execute_and_verify_logs(transaction, expected_log_message) + .await; + } else { + testing_context + .execute_and_verify_transaction(transaction, expected_error) + .await; + } + if expected_error.is_none() { + Some(PrepareOrderResponseFixture { + prepared_order_response: prepared_order_response_pda, + prepared_custody_token: prepared_custody_token_pda, + }) + } else { + None + } +} diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs index c66e0191b..639bed65f 100644 --- a/solana/programs/matching-engine/tests/testing_engine/config.rs +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -63,16 +63,20 @@ impl Default for InitializeFastMarketOrderShimInstructionConfig { #[derive(Clone)] pub struct PrepareOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, + pub solver_index: usize, pub payer_signer: Option>, pub expected_error: Option, + pub expected_log_message: Option, } impl Default for PrepareOrderInstructionConfig { fn default() -> Self { Self { fast_market_order_address: None, + solver_index: 0, payer_signer: None, expected_error: None, + expected_log_message: None, } } } diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 6761cde24..b2e8c3ea2 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -30,6 +30,42 @@ pub enum InstructionTrigger { CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } +/// Functional style testing engine for the matching engine program +/// +/// This engine is used to test the matching engine program with a functional style. +/// Instruction triggers are enums that compose instructions to be executed. +/// Instruction triggers are executed in the order they are provided. +/// The engine is stateful and will track the state of the program. +/// The engine will return the updated state after each instruction trigger. +/// If an instruction trigger fails, the engine will return the previous state. +/// +/// Instruction triggers (enums) take a configuration struct as an argument. +/// The configuration struct contains fields for the expected error, and for +/// providing test specific configuration. +/// +/// Each instruction config struct implements a default constructor. These will expect no errors. +/// +/// Example usage: +/// ```rust +/// // Create a testing context +/// let testing_context = setup_testing_context(//arguments); +/// let testing_engine = TestingEngine::new(testing_context).await; +/// let instruction_triggers = vec![ +/// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +/// InstructionTrigger::CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig::default()), +/// InstructionTrigger::InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig::default()), +/// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +/// InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), +/// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig{ +/// expected_error: Some(ExpectedError{ +/// instruction_index: 0, +/// error_code: 1337, +/// error_message: String::from("LEET error message"), +/// }), +/// }), +/// ]; +/// testing_engine.execute(instruction_triggers).await; +/// ``` pub struct TestingEngine { pub testing_context: TestingContext, } @@ -82,12 +118,12 @@ impl TestingEngine { InstructionTrigger::ExecuteOrderShimless(config) => { self.execute_order_shimless(current_state, config).await } - InstructionTrigger::PrepareOrderShimless(config) => { - self.prepare_order_shimless(current_state, config).await - } InstructionTrigger::PrepareOrderShim(config) => { self.prepare_order_shim(current_state, config).await } + InstructionTrigger::PrepareOrderShimless(config) => { + self.prepare_order_shimless(current_state, config).await + } InstructionTrigger::SettleAuction(config) => { self.settle_auction(current_state, config).await } @@ -361,7 +397,7 @@ impl TestingEngine { auction_state .get_active_auction() .unwrap() - .verify_initial_offer(&self.testing_context.test_context) + .verify_auction(&self.testing_context) .await; return TestingEngineState::InitialOfferPlaced { base: current_state.base().clone(), @@ -471,6 +507,13 @@ impl TestingEngine { .await; if config.expected_error.is_none() { let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); + let active_auction_state = initial_offer_placed_state + .auction_state + .get_active_auction() + .unwrap(); + active_auction_state + .verify_auction(&self.testing_context) + .await; return TestingEngineState::InitialOfferPlaced { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), @@ -652,10 +695,53 @@ impl TestingEngine { async fn prepare_order_shimless( &self, - _current_state: &TestingEngineState, - _config: &PrepareOrderInstructionConfig, + current_state: &TestingEngineState, + config: &PrepareOrderInstructionConfig, ) -> TestingEngineState { - panic!("Not implemented yet"); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let solver_token_account = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index") + .token_account_address() + .expect("Token account does not exist for solver at index"); + let result = shimless::prepare_order_response::prepare_order_response( + &self.testing_context, + &payer_signer, + current_state, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, + &solver_token_account, + config.expected_error.as_ref(), + config.expected_log_message.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let prepare_order_response_fixture = result.unwrap(); + let order_prepared_state = OrderPreparedState { + prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + }; + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } } async fn settle_auction( diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/programs/matching-engine/tests/testing_engine/state.rs index b588da956..8ab6d5fa2 100644 --- a/solana/programs/matching-engine/tests/testing_engine/state.rs +++ b/solana/programs/matching-engine/tests/testing_engine/state.rs @@ -221,6 +221,12 @@ impl TestingEngineState { Self::AuctionSettled { fast_market_order, .. } => fast_market_order.as_ref(), + Self::OrderPrepared { + fast_market_order, .. + } => fast_market_order.as_ref(), + Self::FastMarketOrderClosed { + fast_market_order, .. + } => fast_market_order.as_ref(), _ => None, } } diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index c7f1e11ff..f58bf06ab 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use super::router::TestRouterEndpoints; -use super::setup::{Solver, TransferDirection}; +use super::setup::{Solver, TestingContext, TransferDirection}; use super::Chain; use matching_engine::state::{Auction, AuctionInfo}; use solana_program_test::ProgramTestContext; @@ -71,6 +71,7 @@ impl AuctionAccounts { router_endpoints.get_endpoint_address(Chain::Arbitrum), router_endpoints.get_endpoint_address(Chain::Ethereum), ), + _ => panic!("Unsupported transfer direction"), }; Self { posted_fast_vaa, @@ -87,7 +88,8 @@ impl AuctionAccounts { impl ActiveAuctionState { // TODO: Figure this out - pub async fn verify_initial_offer(&self, test_ctx: &Rc>) { + pub async fn verify_auction(&self, testing_context: &TestingContext) { + let test_ctx = &testing_context.test_context; let auction_account = test_ctx .borrow_mut() .banks_client @@ -99,32 +101,31 @@ impl ActiveAuctionState { let auction_account_data: Auction = AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); let auction_info = auction_account_data.info.unwrap(); + let expected_auction_info = AuctionInfo { - config_id: 0, + config_id: 0, // TODO: Figure this out custody_token_bump: 254, // TODO: Figure this out - vaa_sequence: 0, - source_chain: 23, - best_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata - initial_offer_token: pubkey!("3f3mimemFUZg6o7UuR7AXzt2B5Nh15beCczRPWg8oWnc"), // TODO: Figure this out, I think its the solver's ata + vaa_sequence: 0, // No need to cehck against this + source_chain: { + match testing_context.testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => 3, + TransferDirection::FromArbitrumToEthereum => 23, + _ => panic!("Unsupported transfer direction"), + } + }, + best_offer_token: self.best_offer.offer_token, + initial_offer_token: self.initial_offer.offer_token, start_slot: 1, amount_in: 69000000, security_deposit: 10545000, - offer_price: 1__000_000, + offer_price: self.best_offer.offer_price, redeemer_message_len: 0, destination_asset_info: None, }; assert_eq!(auction_info.config_id, expected_auction_info.config_id); - assert_eq!( - auction_info.source_chain, - expected_auction_info.source_chain - ); assert_eq!(auction_info.start_slot, expected_auction_info.start_slot); - assert_eq!(auction_info.amount_in, expected_auction_info.amount_in); - assert_eq!( - auction_info.security_deposit, - expected_auction_info.security_deposit - ); + assert_eq!(auction_info.offer_price, expected_auction_info.offer_price); assert_eq!( auction_info.redeemer_message_len, diff --git a/solana/programs/matching-engine/tests/utils/cctp_message.rs b/solana/programs/matching-engine/tests/utils/cctp_message.rs index 811e16ce0..a538d9972 100644 --- a/solana/programs/matching-engine/tests/utils/cctp_message.rs +++ b/solana/programs/matching-engine/tests/utils/cctp_message.rs @@ -1,10 +1,14 @@ use crate::utils::ETHEREUM_USDC_ADDRESS; use anchor_lang::prelude::*; -use common::wormhole_cctp_solana::cctp::TOKEN_MESSENGER_MINTER_PROGRAM_ID; +use common::messages::raw::LiquidityLayerDepositMessage; use common::wormhole_cctp_solana::cctp::{ message_transmitter_program::MessageTransmitterConfig, token_messenger_minter_program::RemoteTokenMessenger, }; +use common::wormhole_cctp_solana::cctp::{ + MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, +}; +use common::wormhole_cctp_solana::messages::Deposit; use matching_engine::state::FastMarketOrder; use num_traits::FromBytes; use ruint::Uint; @@ -625,3 +629,42 @@ pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { universal_address } + +pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { + let payload = deposit.payload.clone(); + let liquidity_layer_message = LiquidityLayerDepositMessage::parse(&payload).unwrap(); + let slow_order_response = liquidity_layer_message + .slow_order_response() + .expect("Failed to get slow order response"); + let base_fee = slow_order_response.base_fee(); + base_fee +} + +pub struct UsedNonces; + +impl UsedNonces { + pub const MAX_NONCES: u64 = 6400; + pub fn address(remote_domain: u32, nonce: u64) -> (Pubkey, u8) { + let first_nonce = if nonce == 0 { + 0 + } else { + (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1 + }; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket + let remote_domain_converted = remote_domain.to_string(); + let first_nonce_converted = first_nonce.to_string(); + Pubkey::find_program_address( + &[ + b"used_nonces", + remote_domain_converted.as_bytes(), + first_nonce_converted.as_bytes(), + ], + &MESSAGE_TRANSMITTER_PROGRAM_ID, + ) + } +} + +#[derive(Debug)] +pub struct CctpMessageDecoded { + pub nonce: u64, + pub source_domain: u32, +} diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index dfc6bb4f3..f054f8157 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -7,8 +7,7 @@ pub mod mint; pub mod program_fixtures; pub mod router; pub mod setup; -// pub mod testing_engine; -// pub mod testing_engine_configs; pub mod token_account; +pub mod tracing; pub mod vaa; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index df2bc9b6a..c6bdf3b32 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -7,6 +7,7 @@ use super::program_fixtures::{ initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, initialise_verify_shims, initialise_wormhole_core_bridge, }; +use super::tracing; use super::vaa::{create_vaas_test_with_chain_and_address, TestVaaPair, TestVaaPairs, VaaArgs}; use super::{ airdrop::airdrop_usdc, @@ -204,6 +205,28 @@ impl TestingContext { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } + pub async fn execute_and_verify_logs( + &self, + transaction: impl Into, + expected_log: &String, + ) { + tracing::init_tracing(); + tracing::clear_logs(); + self.test_context + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Transaction should not fail"); + let logs = tracing::get_logs(); + assert!( + logs.contains(expected_log), + "Expected log {:?} not found in {:?}", + expected_log, + logs + ); + } + // TODO: Edit to handle multiple instructions in a single transaction pub async fn execute_and_verify_transaction( &self, @@ -530,10 +553,12 @@ pub enum ShimMode { VerifyAndPostSignature, } +#[allow(dead_code)] #[derive(Copy, Clone)] pub enum TransferDirection { FromArbitrumToEthereum, FromEthereumToArbitrum, + Other, // TODO: Add other transfer directions } impl Default for TransferDirection { @@ -598,6 +623,7 @@ pub async fn setup_environment( vaa_args, )) } + TransferDirection::Other => panic!("Unsupported transfer direction"), } } None => None, diff --git a/solana/programs/matching-engine/tests/utils/testing_engine.rs b/solana/programs/matching-engine/tests/utils/testing_engine.rs deleted file mode 100644 index 1970ff4c6..000000000 --- a/solana/programs/matching-engine/tests/utils/testing_engine.rs +++ /dev/null @@ -1,237 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; - -use crate::shimless::{ - initialize::{initialize_program, InitializeFixture}, - make_offer::{improve_offer, place_initial_offer_shimless}, -}; - -use super::{ - auction::AuctionAccounts, - router::{create_all_router_endpoints_test, TestRouterEndpoints}, - setup::TestingContext, - testing_engine_configs::*, -}; - -#[allow(dead_code)] -pub enum InstructionTrigger { - InitializeProgram(InitializeInstructionConfig), - CreateCctpRouterEndpoints, - InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), - CloseFastMarketOrderShim, - PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), - PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), - ImproveOfferShimless(ImproveOfferInstructionConfig), - ExecuteOrderShimless, - ExecuteOrderShim, - PrepareOrderShimless, - PrepareOrderShim, - SettleAuction, -} - -pub struct InstructionTriggerResults { - pub initialize_program: Option, - pub create_cctp_router_endpoints: Option, -} - -impl InstructionTriggerResults { - pub fn new() -> Self { - Self { - initialize_program: None, - create_cctp_router_endpoints: None, - } - } -} - -pub struct TestingEngine { - pub testing_context: Rc>, - pub instruction_triggers: Vec, - pub instruction_trigger_results: Rc>, -} - -impl TestingEngine { - pub async fn new( - testing_context: TestingContext, - instruction_triggers: Vec, - ) -> Self { - Self { - testing_context: Rc::new(RefCell::new(testing_context)), - instruction_triggers, - instruction_trigger_results: Rc::new(RefCell::new(InstructionTriggerResults::new())), - } - } - - pub async fn execute(&self) { - for trigger in self.instruction_triggers.iter() { - self.execute_trigger(trigger).await; - } - } - - async fn execute_trigger(&self, trigger: &InstructionTrigger) { - match trigger { - InstructionTrigger::InitializeProgram(config) => { - self.instruction_trigger_results - .borrow_mut() - .initialize_program = self.initialize_program(config).await; - } - InstructionTrigger::CreateCctpRouterEndpoints => { - self.instruction_trigger_results - .borrow_mut() - .create_cctp_router_endpoints = self.create_cctp_router_endpoints().await; - } - InstructionTrigger::PlaceInitialOfferShimless(config) => { - self.place_initial_offer_shimless(config).await; - } - InstructionTrigger::ImproveOfferShimless(config) => { - self.improve_offer_shimless(config).await; - } - _ => panic!("Not implemented yet"), // Not implemented yet - } - } - - async fn initialize_program( - &self, - config: &InitializeInstructionConfig, - ) -> Option { - let auction_parameters_config = config.auction_parameters_config.clone(); - let expected_error = config.expected_error.as_ref(); - - let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { - let testing_context_ref = self.testing_context.borrow(); - let result = initialize_program( - &testing_context_ref, - auction_parameters_config, - expected_error, - ) - .await; - - let testing_actors = &testing_context_ref.testing_actors; - ( - result, - testing_actors.owner.pubkey(), - testing_actors.owner_assistant.pubkey(), - testing_actors - .fee_recipient - .token_account_address() - .unwrap(), - ) - }; - - if expected_error.is_none() { - let initialize_fixture = result.expect("Failed to initialize program"); - initialize_fixture.verify_custodian( - owner_pubkey, - owner_assistant_pubkey, - fee_recipient_token_account, - ); - - let auction_config_address = initialize_fixture.get_auction_config_address(); - self.testing_context - .borrow_mut() - .testing_state - .program_state - .initialize(auction_config_address); - return Some(initialize_fixture); - } - None - } - - async fn create_cctp_router_endpoints(&self) -> Option { - let custodian_address = self - .testing_context - .borrow() - .testing_state - .program_state - .get_custodian_address(); - let testing_actors = &self.testing_context.borrow().testing_actors; - let result = create_all_router_endpoints_test( - &self.testing_context.borrow(), - testing_actors.owner.pubkey(), - custodian_address, - testing_actors.owner.keypair(), - ) - .await; - Some(result) - } - - async fn place_initial_offer_shimless(&self, config: &PlaceInitialOfferInstructionConfig) { - let solver = - self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); - let expected_error = config.expected_error.as_ref(); - let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); - let fast_vaa = testing_context - .get_vaa_pair(0) - .expect("Failed to get vaa pair") - .fast_transfer_vaa; - let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); - let custodian_address = testing_context - .testing_state - .program_state - .get_custodian_address(); - let auction_config_address = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap() - .auction_config_address; - - let auction_accounts = AuctionAccounts::new( - Some(fast_vaa_pubkey), - solver, - auction_config_address, - self.instruction_trigger_results - .borrow() - .create_cctp_router_endpoints - .as_ref() - .unwrap(), - custodian_address, - testing_context.get_usdc_mint_address(), - testing_context.testing_state.transfer_direction, - ); - place_initial_offer_shimless( - testing_context, - &auction_accounts, - fast_vaa, - testing_context.get_matching_engine_program_id(), - expected_error, - ) - .await; - if expected_error.is_none() { - self.testing_context - .borrow() - .testing_state - .auction_state - .get_active_auction() - .unwrap() - .verify_initial_offer(&self.testing_context.borrow().test_context) - .await; - } - } - - async fn improve_offer_shimless(&self, config: &ImproveOfferInstructionConfig) { - let expected_error = config.expected_error.as_ref(); - let testing_context: &mut TestingContext = &mut *self.testing_context.borrow_mut(); - let solver = - self.testing_context.borrow().testing_actors.solvers[config.solver_index].clone(); - let offer_price = config.offer_price; - let auction_config_address = testing_context - .testing_state - .auction_state - .get_active_auction() - .unwrap() - .auction_config_address; - improve_offer( - testing_context, - testing_context.get_matching_engine_program_id(), - solver, - auction_config_address, - offer_price, - expected_error, - ) - .await; - // TODO: Implement check on improved offer auction state - // auction_state - // .borrow() - // .verify_improved_offer(&testing_context.test_context) - // .await; - } -} diff --git a/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs b/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs deleted file mode 100644 index e6a5600fc..000000000 --- a/solana/programs/matching-engine/tests/utils/testing_engine_configs.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::shimless::initialize::AuctionParametersConfig; -use matching_engine::error::MatchingEngineError; - -#[derive(Clone)] -pub struct ExpectedError { - pub instruction_index: u8, - pub error: MatchingEngineError, -} - -#[derive(Clone)] -pub struct InitializeInstructionConfig { - pub auction_parameters_config: AuctionParametersConfig, - pub expected_error: Option, -} - -impl Default for InitializeInstructionConfig { - fn default() -> Self { - Self { - auction_parameters_config: AuctionParametersConfig::default(), - expected_error: None, - } - } -} - -#[derive(Clone)] -pub struct InitializeFastMarketOrderShimInstructionConfig { - pub fast_market_order_config: FastMarketOrderConfig, - pub expected_error: Option, -} - -impl Default for InitializeFastMarketOrderShimInstructionConfig { - fn default() -> Self { - Self { - fast_market_order_config: FastMarketOrderConfig::default(), - expected_error: None, - } - } -} - -pub struct PlaceInitialOfferInstructionConfig { - pub solver_index: usize, - pub offer_price: u64, - pub expected_error: Option, -} - -impl Default for PlaceInitialOfferInstructionConfig { - fn default() -> Self { - Self { - solver_index: 0, - offer_price: 1__000_000, - expected_error: None, - } - } -} - -pub struct ImproveOfferInstructionConfig { - pub solver_index: usize, - pub offer_price: u64, - pub expected_error: Option, -} - -impl Default for ImproveOfferInstructionConfig { - fn default() -> Self { - Self { - solver_index: 0, - offer_price: 500_000, - expected_error: None, - } - } -} -#[derive(Clone)] -pub struct FastMarketOrderConfig { - pub fast_market_order_id: u32, -} - -impl Default for FastMarketOrderConfig { - fn default() -> Self { - Self { - fast_market_order_id: 0, - } - } -} diff --git a/solana/programs/matching-engine/tests/utils/tracing.rs b/solana/programs/matching-engine/tests/utils/tracing.rs new file mode 100644 index 000000000..d69bfa754 --- /dev/null +++ b/solana/programs/matching-engine/tests/utils/tracing.rs @@ -0,0 +1,97 @@ +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; +use tracing::subscriber::set_global_default; +use tracing_log::LogTracer; +use tracing_subscriber::{ + fmt::{self, format::FmtSpan}, + layer::SubscriberExt, + EnvFilter, Registry, +}; + +// Global storage for captured logs +static CAPTURED_LOGS: Lazy>>> = + Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); + +// Initialize the tracing subscriber +pub fn init_tracing() { + // Only initialize once + static INITIALIZED: Lazy = Lazy::new(|| { + // Create a custom layer that captures logs + let fmt_layer = fmt::Layer::default() + .with_span_events(FmtSpan::CLOSE) + .with_writer(move || LogCaptureWriter::new(Arc::clone(&CAPTURED_LOGS))); + + // Set up the subscriber with an environment filter + let subscriber = Registry::default() + .with( + EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("solana=debug")), + ) + .with(fmt_layer); + + // Set the subscriber as the global default + set_global_default(subscriber).expect("Failed to set tracing subscriber"); + + // Bridge from log to tracing + LogTracer::init().expect("Failed to initialize log tracer"); + + true + }); + + let _ = *INITIALIZED; +} + +// Clear captured logs +pub fn clear_logs() { + let mut logs = CAPTURED_LOGS.lock().unwrap(); + logs.clear(); +} + +// Get captured logs +pub fn get_logs() -> Vec { + let logs = CAPTURED_LOGS.lock().unwrap(); + logs.clone() +} + +// Get logs containing a specific string +pub fn get_logs_containing(pattern: &str) -> Vec { + let logs = CAPTURED_LOGS.lock().unwrap(); + logs.iter() + .filter(|log| log.contains(pattern)) + .cloned() + .collect() +} + +// Check if logs contain a specific string +pub fn logs_contain(pattern: &str) -> bool { + let logs = CAPTURED_LOGS.lock().unwrap(); + logs.iter().any(|log| log.contains(pattern)) +} + +// Custom writer that captures logs +struct LogCaptureWriter { + logs: Arc>>, +} + +impl LogCaptureWriter { + fn new(logs: Arc>>) -> Self { + Self { logs } + } +} + +impl std::io::Write for LogCaptureWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + if let Ok(log) = std::str::from_utf8(buf) { + let log = log.trim().to_string(); + if !log.is_empty() { + let mut logs = self.logs.lock().unwrap(); + logs.push(log); + } + } + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 1c4961aa2..a800762bb 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -401,7 +401,7 @@ impl TestVaaPair { pub fn create_deposit_message( token_mint: Pubkey, source_address: ChainAddress, - destination_address: ChainAddress, + _destination_address: ChainAddress, cctp_nonce: u64, sequence: u64, cctp_mint_recipient: Pubkey, @@ -415,7 +415,7 @@ pub fn create_deposit_message( token_address: token_mint.to_bytes(), amount: ruint::aliases::U256::from(amount), source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain as usize].1, - destination_cctp_domain: CHAIN_TO_DOMAIN[destination_address.chain as usize].1, + destination_cctp_domain: CHAIN_TO_DOMAIN[Chain::Solana as usize].1, // Hardcode solana as destination domain cctp_nonce, burn_source: source_address.address.to_bytes(), // Token router address mint_recipient: cctp_mint_recipient.to_bytes(), // Mint recipient program id From a1d992da0fa29a4728f12efc724e90d39325aa82 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 12:08:10 +0000 Subject: [PATCH 027/112] all tests passing --- .gitmodules | 2 +- solana/Cargo.lock | 69 +- solana/Cargo.toml | 20 +- solana/lib/wormhole | 2 +- .../tests/integration_tests.rs | 9 +- .../tests/shimful/fast_market_order_shim.rs | 156 ++++ .../matching-engine/tests/shimful/mod.rs | 5 +- .../tests/shimful/post_message.rs | 152 ++++ .../matching-engine/tests/shimful/shims.rs | 698 ------------------ .../tests/shimful/shims_make_offer.rs | 130 ++++ .../shimful/shims_prepare_order_response.rs | 2 +- .../tests/shimful/verify_shim.rs | 283 +++++++ .../tests/testing_engine/engine.rs | 55 +- .../matching-engine/tests/utils/auction.rs | 4 - 14 files changed, 837 insertions(+), 750 deletions(-) create mode 100644 solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs create mode 100644 solana/programs/matching-engine/tests/shimful/post_message.rs delete mode 100644 solana/programs/matching-engine/tests/shimful/shims.rs create mode 100644 solana/programs/matching-engine/tests/shimful/shims_make_offer.rs create mode 100644 solana/programs/matching-engine/tests/shimful/verify_shim.rs diff --git a/.gitmodules b/.gitmodules index 5c3926225..b04c919b6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,4 +13,4 @@ [submodule "solana/lib/wormhole"] path = solana/lib/wormhole url = https://github.com/wormholelabs-xyz/wormhole.git - branch = svm-shims-2 + branch = f69b3ae366211276fe15554f83a2d76abee0535c diff --git a/solana/Cargo.lock b/solana/Cargo.lock index a0acdb684..1e6b7ec59 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -2530,6 +2530,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matching-engine" version = "0.0.0" @@ -2545,6 +2554,8 @@ dependencies = [ "hex-literal", "lazy_static", "liquidity-layer-common-solana", + "num-traits", + "once_cell", "ruint", "secp256k1", "serde", @@ -2553,6 +2564,9 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", + "tracing", + "tracing-log", + "tracing-subscriber", "wormhole-io", "wormhole-solana-utils", "wormhole-svm-definitions", @@ -2713,6 +2727,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.2.1" @@ -2971,6 +2995,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -3450,8 +3480,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -3462,9 +3501,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -6228,6 +6273,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-opentelemetry" version = "0.17.4" @@ -6247,9 +6303,16 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", + "smallvec", "thread_local", + "tracing", "tracing-core", + "tracing-log", ] [[package]] diff --git a/solana/Cargo.toml b/solana/Cargo.toml index fac4220a7..0ea20e14f 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -49,22 +49,22 @@ ruint = "1.9.0" cfg-if = "1.0" hex-literal = "0.4.1" bytemuck = "1.13.0" -wormhole-svm-shim = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "f69b3ae3" } -wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "f69b3ae3", features = ["borsh"] } +wormhole-svm-shim = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "32cb65dd9ae11547f0e57d106b6974dc8ed5f52d" } +wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole.git", rev = "32cb65dd9ae11547f0e57d106b6974dc8ed5f52d", features = ["borsh"] } [patch."https://github.com/wormholelabs-xyz/wormhole.git"] wormhole-svm-shim = { path = "lib/wormhole/svm/wormhole-core-shims/crates/shim" } wormhole-svm-definitions = { path = "lib/wormhole/svm/wormhole-core-shims/crates/definitions" } -[profile.release] -overflow-checks = true -lto = "fat" -codegen-units = 1 +# [profile.release] +# overflow-checks = true +# lto = "fat" +# codegen-units = 1 -[profile.release.build-override] -opt-level = 3 -incremental = false -codegen-units = 1 +# [profile.release.build-override] +# opt-level = 3 +# incremental = false +# codegen-units = 1 [workspace.lints.clippy] correctness = { priority = -1, level = "warn"} diff --git a/solana/lib/wormhole b/solana/lib/wormhole index f69b3ae36..32cb65dd9 160000 --- a/solana/lib/wormhole +++ b/solana/lib/wormhole @@ -1 +1 @@ -Subproject commit f69b3ae366211276fe15554f83a2d76abee0535c +Subproject commit 32cb65dd9ae11547f0e57d106b6974dc8ed5f52d diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index b15764772..70fc64bcf 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -14,7 +14,7 @@ use crate::testing_engine::config::{ PlaceInitialOfferInstructionConfig, }; use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; -use shimful::shims::set_up_post_message_transaction_test; +use shimful::post_message::set_up_post_message_transaction_test; use shimless::initialize::{initialize_program, AuctionParametersConfig}; use solana_sdk::transaction::TransactionError; use utils::router::add_local_router_endpoint_ix; @@ -87,9 +87,8 @@ pub async fn test_local_token_router_endpoint_creation() { .await; } -// Test setting up vaas -// Vaa is from arbitrum to ethereum -// - The payload of the vaa should be the .to_vec() of the FastMarketOrder under universal/rs/messages/src/fast_market_order.rs +/// Test setting up vaas +/// Vaa is from arbitrum to ethereum #[tokio::test] pub async fn test_setup_vaas() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; @@ -227,7 +226,7 @@ pub async fn test_approve_usdc() { // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = - shimful::shims::create_guardian_signatures( + shimful::verify_shim::create_guardian_signatures( &testing_context.test_context, &actors.owner.keypair(), &vaa_data, diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs new file mode 100644 index 000000000..4a0f578d4 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs @@ -0,0 +1,156 @@ +use crate::testing_engine::config::ExpectedError; + +use super::super::utils; +use super::super::utils::constants::*; +use super::super::utils::setup::TestingContext; +use common::messages::FastMarketOrder; +use matching_engine::fallback::close_fast_market_order::{ + CloseFastMarketOrder as CloseFastMarketOrderFallback, + CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, +}; +use matching_engine::fallback::initialise_fast_market_order::{ + InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, + InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, + InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, +}; + +use matching_engine::state::FastMarketOrder as FastMarketOrderState; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; +use std::rc::Rc; +use wormhole_io::TypePrefixedPayload; + +pub fn initialise_fast_market_order_fallback_instruction( + payer_signer: &Rc, + program_id: &Pubkey, + fast_market_order: FastMarketOrderState, + guardian_set_pubkey: Pubkey, + guardian_signatures_pubkey: Pubkey, + guardian_set_bump: u8, +) -> solana_program::instruction::Instruction { + let fast_market_order_account = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.close_account_refund_recipient, + ], + program_id, + ) + .0; + + let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { + signer: &payer_signer.pubkey(), + fast_market_order_account: &fast_market_order_account, + guardian_set: &guardian_set_pubkey, + guardian_set_signatures: &guardian_signatures_pubkey, + verify_vaa_shim_program: &WORMHOLE_VERIFY_VAA_SHIM_PID, + system_program: &solana_program::system_program::ID, + }; + + InitialiseFastMarketOrderFallback { + program_id: program_id, + accounts: create_fast_market_order_accounts, + data: InitialiseFastMarketOrderFallbackData::new(fast_market_order, guardian_set_bump), + } + .instruction() +} + +pub async fn close_fast_market_order_fallback( + testing_context: &TestingContext, + refund_recipient_keypair: &Rc, + program_id: &Pubkey, + fast_market_order_address: &Pubkey, + expected_error: Option<&ExpectedError>, +) { + let test_ctx = &testing_context.test_context; + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let close_fast_market_order_ix = CloseFastMarketOrderFallback { + program_id: program_id, + accounts: CloseFastMarketOrderFallbackAccounts { + fast_market_order: fast_market_order_address, + close_account_refund_recipient: &refund_recipient_keypair.pubkey(), + }, + } + .instruction(); + + let transaction = Transaction::new_signed_with_payer( + &[close_fast_market_order_ix], + Some(&refund_recipient_keypair.pubkey()), + &[refund_recipient_keypair], + recent_blockhash, + ); + testing_context + .execute_and_verify_transaction(transaction, expected_error) + .await; +} + +pub fn create_fast_market_order_state_from_vaa_data( + vaa_data: &utils::vaa::PostedVaaData, + close_account_refund_recipient: Pubkey, +) -> (FastMarketOrderState, utils::vaa::PostedVaaData) { + let vaa_data = utils::vaa::PostedVaaData { + consistency_level: vaa_data.consistency_level, + vaa_time: vaa_data.vaa_time, + sequence: vaa_data.sequence, + emitter_chain: vaa_data.emitter_chain, + emitter_address: vaa_data.emitter_address, + payload: vaa_data.payload.clone(), + nonce: vaa_data.nonce, + vaa_signature_account: vaa_data.vaa_signature_account, + submission_time: 0, + }; + let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( + vaa_data.consistency_level, + vaa_data.vaa_time, + vaa_data.sequence, + vaa_data.emitter_chain, + vaa_data.emitter_address, + ); + + let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); + + let redeemer_message_fixed_length = { + let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) + + if !order.redeemer_message.is_empty() { + // Calculate how many bytes to copy (min of message length and array size) + let copy_len = std::cmp::min(order.redeemer_message.len(), 512); + + // Copy the bytes from the message to the fixed array + fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); + } + + fixed_array + }; + let fast_market_order = FastMarketOrderState::new( + order.amount_in, + order.min_amount_out, + order.deadline, + order.target_chain, + order.redeemer_message.len() as u16, + order.redeemer, + order.sender, + order.refund_address, + order.max_fee, + order.init_auction_fee, + redeemer_message_fixed_length, + close_account_refund_recipient.to_bytes(), + vaa_data.sequence, + vaa_data.vaa_time, + vaa_data.nonce, + vaa_data.emitter_chain, + vaa_data.consistency_level, + vaa_data.emitter_address, + ); + + assert_eq!(fast_market_order.redeemer, order.redeemer); + assert_eq!( + vaa_message.digest(&fast_market_order).as_ref(), + vaa_data.digest().as_ref() + ); + + (fast_market_order, vaa_data) +} diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs index 63aaef9a4..fe6f31bd6 100644 --- a/solana/programs/matching-engine/tests/shimful/mod.rs +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -1,3 +1,6 @@ -pub mod shims; +pub mod fast_market_order_shim; +pub mod post_message; pub mod shims_execute_order; +pub mod shims_make_offer; pub mod shims_prepare_order_response; +pub mod verify_shim; diff --git a/solana/programs/matching-engine/tests/shimful/post_message.rs b/solana/programs/matching-engine/tests/shimful/post_message.rs new file mode 100644 index 000000000..55d3ec3c8 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/post_message.rs @@ -0,0 +1,152 @@ +use crate::utils::constants::*; +use solana_program_test::ProgramTestContext; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + hash::Hash, + message::{v0::Message, VersionedMessage}, + signature::{Keypair, Signer}, + transaction::VersionedTransaction, +}; +use std::cell::RefCell; +use std::rc::Rc; +use wormhole_svm_definitions::{ + find_emitter_sequence_address, find_shim_message_address, solana::Finality, +}; +use wormhole_svm_shim::post_message; + +pub struct BumpCosts { + pub message: u64, + pub sequence: u64, +} + +pub fn bump_cu_cost(bump: u8) -> u64 { + 1_500 * (255 - u64::from(bump)) +} + +pub fn set_up_post_message_transaction( + payload: &[u8], + payer_signer: &Keypair, + emitter_signer: &Keypair, + recent_blockhash: Hash, +) -> (VersionedTransaction, BumpCosts) { + let emitter = emitter_signer.pubkey(); + let payer = payer_signer.pubkey(); + + // Use an invalid message if provided. + let (message, message_bump) = + find_shim_message_address(&emitter, &WORMHOLE_POST_MESSAGE_SHIM_PID); + + // Use an invalid core bridge program if provided. + let core_bridge_program = CORE_BRIDGE_PID; + + let (sequence, sequence_bump) = find_emitter_sequence_address(&emitter, &core_bridge_program); + + let transfer_fee_ix = + solana_sdk::system_instruction::transfer(&payer, &CORE_BRIDGE_FEE_COLLECTOR, 100); + let post_message_ix = post_message::PostMessage { + program_id: &WORMHOLE_POST_MESSAGE_SHIM_PID, + accounts: post_message::PostMessageAccounts { + emitter: &emitter, + payer: &payer, + wormhole_program_id: &core_bridge_program, + derived: post_message::PostMessageDerivedAccounts { + message: Some(&message), + sequence: Some(&sequence), + ..Default::default() + }, + }, + data: post_message::PostMessageData::new(420, Finality::Finalized, payload).unwrap(), + } + .instruction(); + + // Adding compute budget instructions to ensure all instructions fit into + // one transaction. + // + // NOTE: Invoking the compute budget costs in total 300 CU. + let message = Message::try_compile( + &payer, + &[ + transfer_fee_ix, + post_message_ix, + ComputeBudgetInstruction::set_compute_unit_price(420), + ComputeBudgetInstruction::set_compute_unit_limit(100_000), + ], + &[], + recent_blockhash, + ) + .unwrap(); + + let transaction = VersionedTransaction::try_new( + VersionedMessage::V0(message), + &[payer_signer, emitter_signer], + ) + .unwrap(); + + ( + transaction, + BumpCosts { + message: bump_cu_cost(message_bump), + sequence: bump_cu_cost(sequence_bump), + }, + ) +} + +pub async fn set_up_post_message_transaction_test( + test_ctx: &Rc>, + payer_signer: &Rc, + emitter_signer: &Rc, +) { + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Could not get last blockhash"); + let (transaction, _bump_costs) = set_up_post_message_transaction( + b"All your base are belong to us", + &payer_signer.clone().to_owned(), + &emitter_signer.clone().to_owned(), + recent_blockhash, + ); + let details = { + let out = test_ctx + .borrow_mut() + .banks_client + .simulate_transaction(transaction) + .await + .unwrap(); + assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); + out.simulation_details.unwrap() + }; + let logs = details.logs; + let is_core_bridge_cpi_log = + |line: &String| line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()); + // CPI to Core Bridge. + assert_eq!( + logs.iter() + .filter(|line| { + line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) + }) + .count(), + 1 + ); + assert_eq!( + logs.iter() + .filter(|line| { line.contains("Program log: Sequence: 0") }) + .count(), + 1 + ); + let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); + + // Self CPI. + assert_eq!( + logs.iter() + .skip(core_bridge_log_index) + .filter(|line| { + line.contains( + format!("Program {} invoke [2]", WORMHOLE_POST_MESSAGE_SHIM_PID).as_str(), + ) + }) + .count(), + 1 + ); +} diff --git a/solana/programs/matching-engine/tests/shimful/shims.rs b/solana/programs/matching-engine/tests/shimful/shims.rs deleted file mode 100644 index f041c7a11..000000000 --- a/solana/programs/matching-engine/tests/shimful/shims.rs +++ /dev/null @@ -1,698 +0,0 @@ -use crate::testing_engine::config::ExpectedError; -use crate::testing_engine::state::InitialOfferPlacedState; - -use super::super::utils; -use super::super::utils::setup::TestingContext; -use super::super::utils::{constants::*, setup::Solver}; -use anchor_lang::prelude::*; -use base64::Engine; -use common::messages::FastMarketOrder; -use matching_engine::fallback::close_fast_market_order::{ - CloseFastMarketOrder as CloseFastMarketOrderFallback, - CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, -}; -use matching_engine::fallback::initialise_fast_market_order::{ - InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, - InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, - InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, -}; -use matching_engine::fallback::place_initial_offer::{ - PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, - PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, - PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, -}; -use matching_engine::state::{Auction, FastMarketOrder as FastMarketOrderState}; -use solana_program_test::ProgramTestContext; -use solana_sdk::{ - compute_budget::ComputeBudgetInstruction, - hash::Hash, - message::{v0::Message, VersionedMessage}, - pubkey::Pubkey, - signature::Keypair, - signer::Signer, - transaction::{Transaction, VersionedTransaction}, -}; -use std::cell::RefCell; -use std::{rc::Rc, str::FromStr}; -use wormhole_io::TypePrefixedPayload; -use wormhole_svm_definitions::borsh::GuardianSignatures; -use wormhole_svm_definitions::{ - find_emitter_sequence_address, find_shim_message_address, solana::Finality, -}; -use wormhole_svm_shim::{post_message, verify_vaa}; - -#[allow(dead_code)] -struct BumpCosts { - message: u64, - sequence: u64, -} - -fn bump_cu_cost(bump: u8) -> u64 { - 1_500 * (255 - u64::from(bump)) -} - -#[allow(dead_code)] -const EMITTER_SEQUENCE_SEED: &[u8] = b"Sequence"; - -pub async fn set_up_post_message_transaction_test( - test_ctx: &Rc>, - payer_signer: &Rc, - emitter_signer: &Rc, -) { - let recent_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Could not get last blockhash"); - let (transaction, _bump_costs) = set_up_post_message_transaction( - b"All your base are belong to us", - &payer_signer.clone().to_owned(), - &emitter_signer.clone().to_owned(), - recent_blockhash, - ); - let details = { - let out = test_ctx - .borrow_mut() - .banks_client - .simulate_transaction(transaction) - .await - .unwrap(); - assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); - out.simulation_details.unwrap() - }; - let logs = details.logs; - let is_core_bridge_cpi_log = - |line: &String| line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()); - // CPI to Core Bridge. - assert_eq!( - logs.iter() - .filter(|line| { - line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) - }) - .count(), - 1 - ); - assert_eq!( - logs.iter() - .filter(|line| { line.contains("Program log: Sequence: 0") }) - .count(), - 1 - ); - let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); - - // Self CPI. - assert_eq!( - logs.iter() - .skip(core_bridge_log_index) - .filter(|line| { - line.contains( - format!("Program {} invoke [2]", WORMHOLE_POST_MESSAGE_SHIM_PID).as_str(), - ) - }) - .count(), - 1 - ); -} - -fn set_up_post_message_transaction( - payload: &[u8], - payer_signer: &Keypair, - emitter_signer: &Keypair, - recent_blockhash: Hash, -) -> (VersionedTransaction, BumpCosts) { - let emitter = emitter_signer.pubkey(); - let payer = payer_signer.pubkey(); - - // Use an invalid message if provided. - let (message, message_bump) = - find_shim_message_address(&emitter, &WORMHOLE_POST_MESSAGE_SHIM_PID); - - // Use an invalid core bridge program if provided. - let core_bridge_program = CORE_BRIDGE_PID; - - let (sequence, sequence_bump) = find_emitter_sequence_address(&emitter, &core_bridge_program); - - let transfer_fee_ix = - solana_sdk::system_instruction::transfer(&payer, &CORE_BRIDGE_FEE_COLLECTOR, 100); - let post_message_ix = post_message::PostMessage { - program_id: &WORMHOLE_POST_MESSAGE_SHIM_PID, - accounts: post_message::PostMessageAccounts { - emitter: &emitter, - payer: &payer, - wormhole_program_id: &core_bridge_program, - derived: post_message::PostMessageDerivedAccounts { - message: Some(&message), - sequence: Some(&sequence), - ..Default::default() - }, - }, - data: post_message::PostMessageData::new(420, Finality::Finalized, payload).unwrap(), - } - .instruction(); - - // Adding compute budget instructions to ensure all instructions fit into - // one transaction. - // - // NOTE: Invoking the compute budget costs in total 300 CU. - let message = Message::try_compile( - &payer, - &[ - transfer_fee_ix, - post_message_ix, - ComputeBudgetInstruction::set_compute_unit_price(420), - ComputeBudgetInstruction::set_compute_unit_limit(100_000), - ], - &[], - recent_blockhash, - ) - .unwrap(); - - let transaction = VersionedTransaction::try_new( - VersionedMessage::V0(message), - &[payer_signer, emitter_signer], - ) - .unwrap(); - - ( - transaction, - BumpCosts { - message: bump_cu_cost(message_bump), - sequence: bump_cu_cost(sequence_bump), - }, - ) -} - -pub async fn add_guardian_signatures_account( - test_ctx: &Rc>, - payer_signer: &Rc, - signatures_signer: &Rc, - guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, - guardian_set_index: u32, -) -> Result { - let new_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); - let transaction = post_signatures_transaction( - payer_signer, - signatures_signer, - guardian_set_index, - guardian_signatures.len() as u8, - &guardian_signatures, - new_blockhash, - ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await - .expect("Failed to add guardian signatures account"); - - Ok(signatures_signer.pubkey()) -} - -#[allow(dead_code)] -/// Post signatures before the auction is created. -pub async fn set_up_verify_shims_test( - test_ctx: &Rc>, - payer_signer: &Rc, -) -> Result { - let guardian_signatures_signer = Rc::new(Keypair::new()); - let (transaction, decoded_vaa) = - set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); - - let _details = { - let out = test_ctx - .borrow_mut() - .banks_client - .simulate_transaction(transaction.clone()) - .await - .unwrap(); - assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); - assert_eq!( - out.simulation_details.clone().unwrap().units_consumed, - // 13_355 - 3_337 - ); - out.simulation_details.unwrap() - }; - - { - let out = test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await; - assert!(out.is_ok()); - out.unwrap(); - }; - - // Check guardian signatures account after processing the transaction. - let guardian_signatures_info = test_ctx - .borrow_mut() - .banks_client - .get_account(guardian_signatures_signer.pubkey()) - .await - .unwrap() - .unwrap(); - - let account_data = &guardian_signatures_info.data; - let (expected_length, expected_guardian_signatures_data) = - generate_expected_guardian_signatures_info( - &payer_signer.pubkey(), - decoded_vaa.total_signatures, - decoded_vaa.guardian_set_index, - decoded_vaa.guardian_signatures, - ); - - assert_eq!(account_data.len(), expected_length); - assert_eq!( - wormhole_svm_definitions::borsh::deserialize_with_discriminator::< - wormhole_svm_definitions::borsh::GuardianSignatures, - >(&account_data[..]) - .unwrap(), - expected_guardian_signatures_data - ); - Ok(guardian_signatures_signer.pubkey()) -} - -#[derive(Clone)] -#[allow(dead_code)] -struct DecodedVaa { - pub guardian_set_index: u32, - pub total_signatures: u8, - pub guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, - pub body: Vec, -} - -impl From<&str> for DecodedVaa { - fn from(vaa: &str) -> Self { - let mut buf = base64::prelude::BASE64_STANDARD.decode(vaa).unwrap(); - let guardian_set_index = u32::from_be_bytes(buf[1..5].try_into().unwrap()); - let total_signatures = buf[5]; - - let body = buf - .drain( - (6 + total_signatures as usize - * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH).., - ) - .collect(); - - let mut guardian_signatures = Vec::with_capacity(total_signatures as usize); - - for i in 0..usize::from(total_signatures) { - let offset = 6 + i * 66; - let mut signature = [0; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; - signature.copy_from_slice( - &buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH], - ); - guardian_signatures.push(signature); - } - - Self { - guardian_set_index, - total_signatures, - guardian_signatures, - body, - } - } -} - -#[allow(dead_code)] -fn set_up_verify_shims_transaction( - test_ctx: &Rc>, - payer_signer: &Rc, - guardian_signatures_signer: &Rc, -) -> (VersionedTransaction, DecodedVaa) { - const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; - let decoded_vaa = DecodedVaa::from(VAA); - let decoded_vaa_clone = decoded_vaa.clone(); - assert_eq!(decoded_vaa.total_signatures, 13); - let recent_blockhash = test_ctx.borrow().last_blockhash; - let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = - &decoded_vaa.guardian_signatures; - ( - post_signatures_transaction( - payer_signer, - guardian_signatures_signer, - decoded_vaa.guardian_set_index, - decoded_vaa.total_signatures, - guardian_signatures_vec, - recent_blockhash, - ), - decoded_vaa_clone, - ) -} - -fn post_signatures_transaction( - payer_signer: &Rc, - guardian_signatures_signer: &Rc, - guardian_set_index: u32, - total_signatures: u8, - guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, - recent_blockhash: Hash, -) -> VersionedTransaction { - let post_signatures_ix = verify_vaa::PostSignatures { - program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, - accounts: verify_vaa::PostSignaturesAccounts { - payer: &payer_signer.pubkey(), - guardian_signatures: &guardian_signatures_signer.pubkey(), - }, - data: verify_vaa::PostSignaturesData::new( - guardian_set_index, - total_signatures, - guardian_signatures_vec.as_slice(), - ), - } - .instruction(); - - let message = Message::try_compile( - &payer_signer.pubkey(), - &[ - post_signatures_ix, - ComputeBudgetInstruction::set_compute_unit_price(69), - // NOTE: CU limit is higher than needed to resolve errors in test. - ComputeBudgetInstruction::set_compute_unit_limit(25_000), - ], - &[], - recent_blockhash, - ) - .unwrap(); - - VersionedTransaction::try_new( - VersionedMessage::V0(message), - &[payer_signer, guardian_signatures_signer], - ) - .unwrap() -} - -#[allow(dead_code)] -fn generate_expected_guardian_signatures_info( - payer: &Pubkey, - total_signatures: u8, - guardian_set_index: u32, - guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, -) -> ( - usize, // expected length - GuardianSignatures, -) { - let expected_length = { - 8 // discriminator - + 32 // refund recipient - + 4 // guardian set index - + 4 // guardian signatures length - + (total_signatures as usize) * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH - }; - - let guardian_signatures = GuardianSignatures { - refund_recipient: *payer, - guardian_set_index_be: guardian_set_index.to_be_bytes(), - guardian_signatures, - }; - - (expected_length, guardian_signatures) -} - -/// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. -pub async fn place_initial_offer_fallback( - testing_context: &TestingContext, - payer_signer: &Rc, - vaa_data: &utils::vaa::PostedVaaData, - solver: Solver, - fast_market_order_account: &Pubkey, - auction_accounts: &utils::auction::AuctionAccounts, - offer_price: u64, - expected_error: Option<&ExpectedError>, -) -> Option { - let program_id = testing_context.get_matching_engine_program_id(); - let test_ctx = &testing_context.test_context; - let (fast_market_order, vaa_data) = - create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); - - let auction_address = Pubkey::find_program_address( - &[Auction::SEED_PREFIX, &fast_market_order.digest()], - &program_id, - ) - .0; - let auction_custody_token_address = Pubkey::find_program_address( - &[ - matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction_address.as_ref(), - ], - &program_id, - ) - .0; - - // Approve the transfer authority - let transfer_authority = Pubkey::find_program_address( - &[ - common::TRANSFER_AUTHORITY_SEED_PREFIX, - &auction_address.to_bytes(), - &offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; - - solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) - .await; - - let solver_usdc_balance = solver.get_balance(test_ctx).await; - println!("Solver USDC balance: {:?}", solver_usdc_balance); - - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( - offer_price, - vaa_data.sequence, - vaa_data.vaa_time, - vaa_data.consistency_level, - ); - - let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { - signer: &payer_signer.pubkey(), - transfer_authority: &transfer_authority, - custodian: &auction_accounts.custodian, - auction_config: &auction_accounts.auction_config, - from_endpoint: &auction_accounts.from_router_endpoint, - to_endpoint: &auction_accounts.to_router_endpoint, - fast_market_order: fast_market_order_account, - auction: &auction_address, - offer_token: &auction_accounts.offer_token, - auction_custody_token: &auction_custody_token_address, - usdc: &auction_accounts.usdc_mint, - system_program: &solana_program::system_program::ID, - token_program: &anchor_spl::token::spl_token::ID, - }; - let place_initial_offer_ix = PlaceInitialOfferCctpShimFallback { - program_id: &program_id, - accounts: place_initial_offer_ix_accounts, - data: place_initial_offer_ix_data, - } - .instruction(); - - let recent_blockhash = test_ctx.borrow().last_blockhash; - - let transaction = Transaction::new_signed_with_payer( - &[place_initial_offer_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - recent_blockhash, - ); - - testing_context - .execute_and_verify_transaction(transaction, expected_error) - .await; - if expected_error.is_none() { - let new_active_auction_state = utils::auction::ActiveAuctionState { - auction_address, - auction_custody_token_address, - auction_config_address: auction_accounts.auction_config, - initial_offer: utils::auction::AuctionOffer { - participant: payer_signer.pubkey(), - offer_token: auction_accounts.offer_token, - offer_price, - }, - best_offer: utils::auction::AuctionOffer { - participant: payer_signer.pubkey(), - offer_token: auction_accounts.offer_token, - offer_price, - }, - }; - let new_auction_state = utils::auction::AuctionState::Active(new_active_auction_state); - Some(InitialOfferPlacedState { - auction_state: new_auction_state, - }) - } else { - None - } -} - -pub fn initialise_fast_market_order_fallback_instruction( - payer_signer: &Rc, - program_id: &Pubkey, - fast_market_order: FastMarketOrderState, - guardian_set_pubkey: Pubkey, - guardian_signatures_pubkey: Pubkey, - guardian_set_bump: u8, -) -> solana_program::instruction::Instruction { - let fast_market_order_account = Pubkey::find_program_address( - &[ - FastMarketOrderState::SEED_PREFIX, - &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient, - ], - program_id, - ) - .0; - - let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { - signer: &payer_signer.pubkey(), - fast_market_order_account: &fast_market_order_account, - guardian_set: &guardian_set_pubkey, - guardian_set_signatures: &guardian_signatures_pubkey, - verify_vaa_shim_program: &WORMHOLE_VERIFY_VAA_SHIM_PID, - system_program: &solana_program::system_program::ID, - }; - - InitialiseFastMarketOrderFallback { - program_id: program_id, - accounts: create_fast_market_order_accounts, - data: InitialiseFastMarketOrderFallbackData::new(fast_market_order, guardian_set_bump), - } - .instruction() -} - -pub async fn close_fast_market_order_fallback( - testing_context: &TestingContext, - refund_recipient_keypair: &Rc, - program_id: &Pubkey, - fast_market_order_address: &Pubkey, - expected_error: Option<&ExpectedError>, -) { - let test_ctx = &testing_context.test_context; - let recent_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); - let close_fast_market_order_ix = CloseFastMarketOrderFallback { - program_id: program_id, - accounts: CloseFastMarketOrderFallbackAccounts { - fast_market_order: fast_market_order_address, - close_account_refund_recipient: &refund_recipient_keypair.pubkey(), - }, - } - .instruction(); - - let transaction = Transaction::new_signed_with_payer( - &[close_fast_market_order_ix], - Some(&refund_recipient_keypair.pubkey()), - &[refund_recipient_keypair], - recent_blockhash, - ); - testing_context - .execute_and_verify_transaction(transaction, expected_error) - .await; -} - -pub fn create_fast_market_order_state_from_vaa_data( - vaa_data: &utils::vaa::PostedVaaData, - close_account_refund_recipient: Pubkey, -) -> (FastMarketOrderState, utils::vaa::PostedVaaData) { - let vaa_data = utils::vaa::PostedVaaData { - consistency_level: vaa_data.consistency_level, - vaa_time: vaa_data.vaa_time, - sequence: vaa_data.sequence, - emitter_chain: vaa_data.emitter_chain, - emitter_address: vaa_data.emitter_address, - payload: vaa_data.payload.clone(), - nonce: vaa_data.nonce, - vaa_signature_account: vaa_data.vaa_signature_account, - submission_time: 0, - }; - let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( - vaa_data.consistency_level, - vaa_data.vaa_time, - vaa_data.sequence, - vaa_data.emitter_chain, - vaa_data.emitter_address, - ); - - let order: FastMarketOrder = TypePrefixedPayload::<1>::read_slice(&vaa_data.payload).unwrap(); - - let redeemer_message_fixed_length = { - let mut fixed_array = [0u8; 512]; // Initialize with zeros (automatic padding) - - if !order.redeemer_message.is_empty() { - // Calculate how many bytes to copy (min of message length and array size) - let copy_len = std::cmp::min(order.redeemer_message.len(), 512); - - // Copy the bytes from the message to the fixed array - fixed_array[..copy_len].copy_from_slice(&order.redeemer_message[..copy_len]); - } - - fixed_array - }; - let fast_market_order = FastMarketOrderState::new( - order.amount_in, - order.min_amount_out, - order.deadline, - order.target_chain, - order.redeemer_message.len() as u16, - order.redeemer, - order.sender, - order.refund_address, - order.max_fee, - order.init_auction_fee, - redeemer_message_fixed_length, - close_account_refund_recipient.to_bytes(), - vaa_data.sequence, - vaa_data.vaa_time, - vaa_data.nonce, - vaa_data.emitter_chain, - vaa_data.consistency_level, - vaa_data.emitter_address, - ); - - assert_eq!(fast_market_order.redeemer, order.redeemer); - assert_eq!( - vaa_message.digest(&fast_market_order).as_ref(), - vaa_data.digest().as_ref() - ); - - (fast_market_order, vaa_data) -} - -pub async fn create_guardian_signatures( - test_ctx: &Rc>, - payer_signer: &Rc, - vaa_data: &utils::vaa::PostedVaaData, - wormhole_program_id: &Pubkey, - guardian_signature_signer: Option<&Rc>, -) -> (Pubkey, Pubkey, u8) { - let new_keypair = Rc::new(Keypair::new()); - let guardian_signature_signer = guardian_signature_signer.unwrap_or(&new_keypair); - let (guardian_set_pubkey, guardian_set_bump) = - wormhole_svm_definitions::find_guardian_set_address( - 0_u32.to_be_bytes(), - &wormhole_program_id, - ); - let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY) - .expect("Failed to parse guardian secret key"); - let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); - let guardian_signatures_pubkey = add_guardian_signatures_account( - test_ctx, - payer_signer, - guardian_signature_signer, - vec![guardian_set_signatures], - 0, - ) - .await - .expect("Failed to post guardian signatures"); - ( - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, - ) -} diff --git a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs new file mode 100644 index 000000000..6c4807c74 --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs @@ -0,0 +1,130 @@ +use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::state::InitialOfferPlacedState; + +use super::super::utils; +use super::super::utils::setup::Solver; +use super::super::utils::setup::TestingContext; +use matching_engine::fallback::place_initial_offer::{ + PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, + PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, + PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, +}; +use matching_engine::state::Auction; + +use super::fast_market_order_shim::create_fast_market_order_state_from_vaa_data; +use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; +use std::rc::Rc; + +/// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. +pub async fn place_initial_offer_fallback( + testing_context: &TestingContext, + payer_signer: &Rc, + vaa_data: &utils::vaa::PostedVaaData, + solver: Solver, + fast_market_order_account: &Pubkey, + auction_accounts: &utils::auction::AuctionAccounts, + offer_price: u64, + expected_error: Option<&ExpectedError>, +) -> Option { + let program_id = testing_context.get_matching_engine_program_id(); + let test_ctx = &testing_context.test_context; + let (fast_market_order, vaa_data) = + create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); + + let auction_address = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, &fast_market_order.digest()], + &program_id, + ) + .0; + let auction_custody_token_address = Pubkey::find_program_address( + &[ + matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction_address.as_ref(), + ], + &program_id, + ) + .0; + + // Approve the transfer authority + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &auction_address.to_bytes(), + &offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + + solver + .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .await; + + let solver_usdc_balance = solver.get_balance(test_ctx).await; + println!("Solver USDC balance: {:?}", solver_usdc_balance); + + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( + offer_price, + vaa_data.sequence, + vaa_data.vaa_time, + vaa_data.consistency_level, + ); + + let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { + signer: &payer_signer.pubkey(), + transfer_authority: &transfer_authority, + custodian: &auction_accounts.custodian, + auction_config: &auction_accounts.auction_config, + from_endpoint: &auction_accounts.from_router_endpoint, + to_endpoint: &auction_accounts.to_router_endpoint, + fast_market_order: fast_market_order_account, + auction: &auction_address, + offer_token: &auction_accounts.offer_token, + auction_custody_token: &auction_custody_token_address, + usdc: &auction_accounts.usdc_mint, + system_program: &solana_program::system_program::ID, + token_program: &anchor_spl::token::spl_token::ID, + }; + let place_initial_offer_ix = PlaceInitialOfferCctpShimFallback { + program_id: &program_id, + accounts: place_initial_offer_ix_accounts, + data: place_initial_offer_ix_data, + } + .instruction(); + + let recent_blockhash = test_ctx.borrow().last_blockhash; + + let transaction = Transaction::new_signed_with_payer( + &[place_initial_offer_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + recent_blockhash, + ); + + testing_context + .execute_and_verify_transaction(transaction, expected_error) + .await; + if expected_error.is_none() { + let new_active_auction_state = utils::auction::ActiveAuctionState { + auction_address, + auction_custody_token_address, + auction_config_address: auction_accounts.auction_config, + initial_offer: utils::auction::AuctionOffer { + participant: payer_signer.pubkey(), + offer_token: auction_accounts.offer_token, + offer_price, + }, + best_offer: utils::auction::AuctionOffer { + participant: payer_signer.pubkey(), + offer_token: auction_accounts.offer_token, + offer_price, + }, + }; + let new_auction_state = utils::auction::AuctionState::Active(new_active_auction_state); + Some(InitialOfferPlacedState { + auction_state: new_auction_state, + }) + } else { + None + } +} diff --git a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs index 6e537c8b0..22fe8b19c 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs @@ -292,7 +292,7 @@ pub async fn prepare_order_response_test( .expect("Fixture accounts not found"); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - super::shims::create_guardian_signatures( + super::verify_shim::create_guardian_signatures( test_ctx, payer_signer, deposit_vaa_data, diff --git a/solana/programs/matching-engine/tests/shimful/verify_shim.rs b/solana/programs/matching-engine/tests/shimful/verify_shim.rs new file mode 100644 index 000000000..2f68c31fe --- /dev/null +++ b/solana/programs/matching-engine/tests/shimful/verify_shim.rs @@ -0,0 +1,283 @@ +use crate::utils; +use crate::utils::constants::*; +use anchor_lang::prelude::*; +use base64::Engine; +use solana_program_test::ProgramTestContext; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + hash::Hash, + message::{v0::Message, VersionedMessage}, + signature::{Keypair, Signer}, + transaction::VersionedTransaction, +}; +use std::cell::RefCell; +use std::rc::Rc; +use std::str::FromStr; +use wormhole_svm_definitions::borsh::GuardianSignatures; +use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; +use wormhole_svm_shim::verify_vaa; + +pub async fn create_guardian_signatures( + test_ctx: &Rc>, + payer_signer: &Rc, + vaa_data: &utils::vaa::PostedVaaData, + wormhole_program_id: &Pubkey, + guardian_signature_signer: Option<&Rc>, +) -> (Pubkey, Pubkey, u8) { + let new_keypair = Rc::new(Keypair::new()); + let guardian_signature_signer = guardian_signature_signer.unwrap_or(&new_keypair); + let (guardian_set_pubkey, guardian_set_bump) = + wormhole_svm_definitions::find_guardian_set_address( + 0_u32.to_be_bytes(), + &wormhole_program_id, + ); + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY) + .expect("Failed to parse guardian secret key"); + let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); + let guardian_signatures_pubkey = add_guardian_signatures_account( + test_ctx, + payer_signer, + guardian_signature_signer, + vec![guardian_set_signatures], + 0, + ) + .await + .expect("Failed to post guardian signatures"); + ( + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ) +} + +pub async fn add_guardian_signatures_account( + test_ctx: &Rc>, + payer_signer: &Rc, + signatures_signer: &Rc, + guardian_signatures: Vec<[u8; GUARDIAN_SIGNATURE_LENGTH]>, + guardian_set_index: u32, +) -> Result { + let new_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let transaction = post_signatures_transaction( + payer_signer, + signatures_signer, + guardian_set_index, + guardian_signatures.len() as u8, + &guardian_signatures, + new_blockhash, + ); + test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to add guardian signatures account"); + + Ok(signatures_signer.pubkey()) +} + +fn post_signatures_transaction( + payer_signer: &Rc, + guardian_signatures_signer: &Rc, + guardian_set_index: u32, + total_signatures: u8, + guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, + recent_blockhash: Hash, +) -> VersionedTransaction { + let post_signatures_ix = verify_vaa::PostSignatures { + program_id: &WORMHOLE_VERIFY_VAA_SHIM_PID, + accounts: verify_vaa::PostSignaturesAccounts { + payer: &payer_signer.pubkey(), + guardian_signatures: &guardian_signatures_signer.pubkey(), + }, + data: verify_vaa::PostSignaturesData::new( + guardian_set_index, + total_signatures, + guardian_signatures_vec.as_slice(), + ), + } + .instruction(); + + let message = Message::try_compile( + &payer_signer.pubkey(), + &[ + post_signatures_ix, + ComputeBudgetInstruction::set_compute_unit_price(69), + // NOTE: CU limit is higher than needed to resolve errors in test. + ComputeBudgetInstruction::set_compute_unit_limit(25_000), + ], + &[], + recent_blockhash, + ) + .unwrap(); + + VersionedTransaction::try_new( + VersionedMessage::V0(message), + &[payer_signer, guardian_signatures_signer], + ) + .unwrap() +} + +#[allow(dead_code)] +/// Post signatures before the auction is created. +pub async fn set_up_verify_shims_test( + test_ctx: &Rc>, + payer_signer: &Rc, +) -> Result { + let guardian_signatures_signer = Rc::new(Keypair::new()); + let (transaction, decoded_vaa) = + set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); + + let _details = { + let out = test_ctx + .borrow_mut() + .banks_client + .simulate_transaction(transaction.clone()) + .await + .unwrap(); + assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); + assert_eq!( + out.simulation_details.clone().unwrap().units_consumed, + // 13_355 + 3_337 + ); + out.simulation_details.unwrap() + }; + + { + let out = test_ctx + .borrow_mut() + .banks_client + .process_transaction(transaction) + .await; + assert!(out.is_ok()); + out.unwrap(); + }; + + // Check guardian signatures account after processing the transaction. + let guardian_signatures_info = test_ctx + .borrow_mut() + .banks_client + .get_account(guardian_signatures_signer.pubkey()) + .await + .unwrap() + .unwrap(); + + let account_data = &guardian_signatures_info.data; + let (expected_length, expected_guardian_signatures_data) = + generate_expected_guardian_signatures_info( + &payer_signer.pubkey(), + decoded_vaa.total_signatures, + decoded_vaa.guardian_set_index, + decoded_vaa.guardian_signatures, + ); + + assert_eq!(account_data.len(), expected_length); + assert_eq!( + wormhole_svm_definitions::borsh::deserialize_with_discriminator::< + wormhole_svm_definitions::borsh::GuardianSignatures, + >(&account_data[..]) + .unwrap(), + expected_guardian_signatures_data + ); + Ok(guardian_signatures_signer.pubkey()) +} + +#[allow(dead_code)] +fn set_up_verify_shims_transaction( + test_ctx: &Rc>, + payer_signer: &Rc, + guardian_signatures_signer: &Rc, +) -> (VersionedTransaction, DecodedVaa) { + const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; + let decoded_vaa = DecodedVaa::from(VAA); + let decoded_vaa_clone = decoded_vaa.clone(); + assert_eq!(decoded_vaa.total_signatures, 13); + let recent_blockhash = test_ctx.borrow().last_blockhash; + let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = + &decoded_vaa.guardian_signatures; + ( + post_signatures_transaction( + payer_signer, + guardian_signatures_signer, + decoded_vaa.guardian_set_index, + decoded_vaa.total_signatures, + guardian_signatures_vec, + recent_blockhash, + ), + decoded_vaa_clone, + ) +} + +#[allow(dead_code)] +fn generate_expected_guardian_signatures_info( + payer: &Pubkey, + total_signatures: u8, + guardian_set_index: u32, + guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, +) -> ( + usize, // expected length + GuardianSignatures, +) { + let expected_length = { + 8 // discriminator + + 32 // refund recipient + + 4 // guardian set index + + 4 // guardian signatures length + + (total_signatures as usize) * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH + }; + + let guardian_signatures = GuardianSignatures { + refund_recipient: *payer, + guardian_set_index_be: guardian_set_index.to_be_bytes(), + guardian_signatures, + }; + + (expected_length, guardian_signatures) +} + +#[derive(Clone)] +#[allow(dead_code)] +struct DecodedVaa { + pub guardian_set_index: u32, + pub total_signatures: u8, + pub guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, + pub body: Vec, +} + +impl From<&str> for DecodedVaa { + fn from(vaa: &str) -> Self { + let mut buf = base64::prelude::BASE64_STANDARD.decode(vaa).unwrap(); + let guardian_set_index = u32::from_be_bytes(buf[1..5].try_into().unwrap()); + let total_signatures = buf[5]; + + let body = buf + .drain( + (6 + total_signatures as usize + * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH).., + ) + .collect(); + + let mut guardian_signatures = Vec::with_capacity(total_signatures as usize); + + for i in 0..usize::from(total_signatures) { + let offset = 6 + i * 66; + let mut signature = [0; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; + signature.copy_from_slice( + &buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH], + ); + guardian_signatures.push(signature); + } + + Self { + guardian_set_index, + total_signatures, + guardian_signatures, + body, + } + } +} diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index b2e8c3ea2..6a5d7da10 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -3,15 +3,17 @@ use solana_sdk::transaction::VersionedTransaction; use super::{config::*, state::*}; use crate::shimful; -use crate::shimful::shims::{ - create_fast_market_order_state_from_vaa_data, create_guardian_signatures, - initialise_fast_market_order_fallback_instruction, +use crate::shimful::fast_market_order_shim::{ + create_fast_market_order_state_from_vaa_data, initialise_fast_market_order_fallback_instruction, }; -use crate::utils::auction::AuctionState; +use crate::shimful::verify_shim::create_guardian_signatures; +use crate::shimless; +use crate::utils::vaa::TestVaaPairs; use crate::utils::{ - auction::AuctionAccounts, router::create_all_router_endpoints_test, setup::TestingContext, + auction::{AuctionAccounts, AuctionState}, + router::create_all_router_endpoints_test, + setup::TestingContext, }; -use crate::{shimless, utils::vaa::TestVaaPairs}; use anchor_lang::prelude::*; #[allow(dead_code)] @@ -98,6 +100,14 @@ impl TestingEngine { self.create_cctp_router_endpoints(current_state, config) .await } + InstructionTrigger::InitializeFastMarketOrderShim(config) => { + self.create_fast_market_order_account(current_state, config) + .await + } + InstructionTrigger::CloseFastMarketOrderShim(config) => { + self.close_fast_market_order_account(current_state, config) + .await + } InstructionTrigger::PlaceInitialOfferShimless(config) => { self.place_initial_offer_shimless(current_state, config) .await @@ -105,10 +115,6 @@ impl TestingEngine { InstructionTrigger::PlaceInitialOfferShim(config) => { self.place_initial_offer_shim(current_state, config).await } - InstructionTrigger::InitializeFastMarketOrderShim(config) => { - self.create_fast_market_order_account(current_state, config) - .await - } InstructionTrigger::ImproveOfferShimless(config) => { self.improve_offer_shimless(current_state, config).await } @@ -127,10 +133,6 @@ impl TestingEngine { InstructionTrigger::SettleAuction(config) => { self.settle_auction(current_state, config).await } - InstructionTrigger::CloseFastMarketOrderShim(config) => { - self.close_fast_market_order_account(current_state, config) - .await - } } } @@ -317,7 +319,7 @@ impl TestingEngine { .clone() .unwrap_or(self.testing_context.testing_actors.solvers[0].keypair()); - shimful::shims::close_fast_market_order_fallback( + shimful::fast_market_order_shim::close_fast_market_order_fallback( &self.testing_context, &close_account_refund_recipient, &self.testing_context.get_matching_engine_program_id(), @@ -494,17 +496,18 @@ impl TestingEngine { .get_first_test_vaa_pair() .fast_transfer_vaa .get_vaa_data(); - let place_initial_offer_shim_fixture = shimful::shims::place_initial_offer_fallback( - &self.testing_context, - &payer_signer, - &fast_vaa_data, - solver, - &fast_market_order_address, - &auction_accounts, - config.offer_price, - config.expected_error.as_ref(), - ) - .await; + let place_initial_offer_shim_fixture = + shimful::shims_make_offer::place_initial_offer_fallback( + &self.testing_context, + &payer_signer, + &fast_vaa_data, + solver, + &fast_market_order_address, + &auction_accounts, + config.offer_price, + config.expected_error.as_ref(), + ) + .await; if config.expected_error.is_none() { let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); let active_auction_state = initial_offer_placed_state diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index f58bf06ab..af1f2c42d 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -4,9 +4,6 @@ use super::router::TestRouterEndpoints; use super::setup::{Solver, TestingContext, TransferDirection}; use super::Chain; use matching_engine::state::{Auction, AuctionInfo}; -use solana_program_test::ProgramTestContext; -use std::cell::RefCell; -use std::rc::Rc; #[derive(Clone)] pub struct AuctionAccounts { @@ -87,7 +84,6 @@ impl AuctionAccounts { } impl ActiveAuctionState { - // TODO: Figure this out pub async fn verify_auction(&self, testing_context: &TestingContext) { let test_ctx = &testing_context.test_context; let auction_account = test_ctx From 1b723078c49e44edda3da082f0b3b0a6fbdff8f1 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 12:23:03 +0000 Subject: [PATCH 028/112] only the create and close fast market order instructions included --- .../tests/integration_tests.rs | 378 +------------- .../matching-engine/tests/shimful/mod.rs | 6 +- .../matching-engine/tests/shimless/mod.rs | 8 +- .../tests/testing_engine/engine.rs | 485 +----------------- 4 files changed, 9 insertions(+), 868 deletions(-) diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index 70fc64bcf..fb2f835eb 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -1,6 +1,5 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; -use matching_engine::error::MatchingEngineError; use matching_engine::ID as PROGRAM_ID; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -9,14 +8,10 @@ mod shimful; mod shimless; mod testing_engine; mod utils; -use crate::testing_engine::config::{ - ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, - PlaceInitialOfferInstructionConfig, -}; +use crate::testing_engine::config::InitializeInstructionConfig; use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; use shimful::post_message::set_up_post_message_transaction_test; use shimless::initialize::{initialize_program, AuctionParametersConfig}; -use solana_sdk::transaction::TransactionError; use utils::router::add_local_router_endpoint_ix; use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; use utils::vaa::VaaArgs; @@ -108,10 +103,6 @@ pub async fn test_setup_vaas() { InstructionTrigger::CreateCctpRouterEndpoints( CreateCctpRouterEndpointsInstructionConfig::default(), ), - InstructionTrigger::PlaceInitialOfferShimless( - PlaceInitialOfferInstructionConfig::default(), - ), - InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), ]) .await; } @@ -249,370 +240,3 @@ pub async fn test_approve_usdc() { TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); assert!(solver_token_account.delegate.is_some()); } - -#[tokio::test] -// Testing a initial offer from arbitrum to ethereum -// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction -pub async fn test_place_initial_offer_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let testing_engine = TestingEngine::new(testing_context).await; - - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig { - solver_index: 1, - ..ImproveOfferInstructionConfig::default() - }), - ]; - - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_shim_blocks_non_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { - solver_index: 0, - ..PlaceInitialOfferInstructionConfig::default() - }), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { - solver_index: 1, - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PlaceInitialOfferInstructionConfig::default() - }), - ]; - - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_non_shim_blocks_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { - solver_index: 0, - ..PlaceInitialOfferInstructionConfig::default() - }), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { - solver_index: 1, - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PlaceInitialOfferInstructionConfig::default() - }), - ]; - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -// Testing an execute order from arbitrum to ethereum -// TODO: Flesh out this test to see if the message was posted correctly -pub async fn test_execute_order_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - ]; - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_execute_order_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - ]; - testing_engine.execute(instruction_triggers).await; -} -pub async fn test_execute_order_fallback_blocks_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: MatchingEngineError::AccountAlreadyInitialized.into(), - error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), - }), - ..ExecuteOrderInstructionConfig::default() - }), - ]; - testing_engine.execute(instruction_triggers).await; -} - -// From ethereum to arbitrum -#[tokio::test] -pub async fn test_prepare_order_shim_fallback() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - ]; - testing_engine.execute(instruction_triggers).await; -} - -// Prepare order response from ethereum to arbitrum (shimless) -#[tokio::test] -pub async fn test_prepare_order_shimless() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), - ]; - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_prepare_order_response_shimful_blocks_shimless() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { - // expected_log_message: Some("Already prepared".to_string()), - ..PrepareOrderInstructionConfig::default() - }), - ]; - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_prepare_order_response_shimless_blocks_shimful() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), - // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PrepareOrderInstructionConfig::default() - }), - ]; - testing_engine.execute(instruction_triggers).await; -} - -#[tokio::test] -pub async fn test_settle_auction_complete() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let testing_context = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), - ]; - testing_engine.execute(instruction_triggers).await; -} diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs index fe6f31bd6..4718fadfa 100644 --- a/solana/programs/matching-engine/tests/shimful/mod.rs +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -1,6 +1,6 @@ pub mod fast_market_order_shim; pub mod post_message; -pub mod shims_execute_order; -pub mod shims_make_offer; -pub mod shims_prepare_order_response; +// pub mod shims_execute_order; +// pub mod shims_make_offer; +// pub mod shims_prepare_order_response; pub mod verify_shim; diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs index 9da720703..3eb39ba62 100644 --- a/solana/programs/matching-engine/tests/shimless/mod.rs +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -1,5 +1,5 @@ -pub mod execute_order; +// pub mod execute_order; pub mod initialize; -pub mod make_offer; -pub mod prepare_order_response; -pub mod settle_auction; +// pub mod make_offer; +// pub mod prepare_order_response; +// pub mod settle_auction; diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 6a5d7da10..689eb6865 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -9,11 +9,7 @@ use crate::shimful::fast_market_order_shim::{ use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; use crate::utils::vaa::TestVaaPairs; -use crate::utils::{ - auction::{AuctionAccounts, AuctionState}, - router::create_all_router_endpoints_test, - setup::TestingContext, -}; +use crate::utils::{router::create_all_router_endpoints_test, setup::TestingContext}; use anchor_lang::prelude::*; #[allow(dead_code)] @@ -21,14 +17,6 @@ pub enum InstructionTrigger { InitializeProgram(InitializeInstructionConfig), CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig), InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), - PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), - PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), - ImproveOfferShimless(ImproveOfferInstructionConfig), - ExecuteOrderShimless(ExecuteOrderInstructionConfig), - ExecuteOrderShim(ExecuteOrderInstructionConfig), - PrepareOrderShimless(PrepareOrderInstructionConfig), - PrepareOrderShim(PrepareOrderInstructionConfig), - SettleAuction(SettleAuctionInstructionConfig), CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } @@ -108,31 +96,6 @@ impl TestingEngine { self.close_fast_market_order_account(current_state, config) .await } - InstructionTrigger::PlaceInitialOfferShimless(config) => { - self.place_initial_offer_shimless(current_state, config) - .await - } - InstructionTrigger::PlaceInitialOfferShim(config) => { - self.place_initial_offer_shim(current_state, config).await - } - InstructionTrigger::ImproveOfferShimless(config) => { - self.improve_offer_shimless(current_state, config).await - } - InstructionTrigger::ExecuteOrderShim(config) => { - self.execute_order_shim(current_state, config).await - } - InstructionTrigger::ExecuteOrderShimless(config) => { - self.execute_order_shimless(current_state, config).await - } - InstructionTrigger::PrepareOrderShim(config) => { - self.prepare_order_shim(current_state, config).await - } - InstructionTrigger::PrepareOrderShimless(config) => { - self.prepare_order_shimless(current_state, config).await - } - InstructionTrigger::SettleAuction(config) => { - self.settle_auction(current_state, config).await - } } } @@ -338,450 +301,4 @@ impl TestingEngine { auction_accounts: current_state.auction_accounts().cloned(), } } - async fn place_initial_offer_shimless( - &self, - current_state: &TestingEngineState, - config: &PlaceInitialOfferInstructionConfig, - ) -> TestingEngineState { - assert!( - current_state.router_endpoints().is_some(), - "Router endpoints are not created" - ); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let solver = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index"); - let expected_error = config.expected_error.as_ref(); - let fast_vaa = ¤t_state - .base() - .vaas - .get(0) - .expect("Failed to get vaa pair") - .fast_transfer_vaa; - let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); - let auction_config_address = current_state - .initialized() - .expect("Testing state is not initialized") - .auction_config_address; - let custodian_address = current_state - .initialized() - .expect("Testing state is not initialized") - .custodian_address; - let auction_accounts = AuctionAccounts::new( - Some(fast_vaa_pubkey), - solver.clone(), - auction_config_address, - ¤t_state - .router_endpoints() - .expect("Router endpoints are not created") - .endpoints, - custodian_address, - self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, - ); - let auction_state = shimless::make_offer::place_initial_offer_shimless( - &self.testing_context, - &auction_accounts, - fast_vaa, - config.offer_price, - &payer_signer, - self.testing_context.get_matching_engine_program_id(), - expected_error, - ) - .await; - if expected_error.is_none() { - auction_state - .get_active_auction() - .unwrap() - .verify_auction(&self.testing_context) - .await; - return TestingEngineState::InitialOfferPlaced { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state, - auction_accounts, - }; - } - current_state.clone() - } - - async fn improve_offer_shimless( - &self, - current_state: &TestingEngineState, - config: &ImproveOfferInstructionConfig, - ) -> TestingEngineState { - let expected_error = config.expected_error.as_ref(); - let solver = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index"); - let offer_price = config.offer_price; - let auction_config_address = current_state - .auction_config_address() - .expect("Auction config address not found"); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let new_auction_state = shimless::make_offer::improve_offer( - &self.testing_context, - self.testing_context.get_matching_engine_program_id(), - solver.clone(), - auction_config_address, - offer_price, - &payer_signer, - current_state.auction_state(), - expected_error, - ) - .await; - if expected_error.is_none() { - let auction_state = new_auction_state.unwrap(); - return TestingEngineState::OfferImproved { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state, - auction_accounts: current_state.auction_accounts().cloned(), - }; - } - current_state.clone() - } - - async fn place_initial_offer_shim( - &self, - current_state: &TestingEngineState, - config: &PlaceInitialOfferInstructionConfig, - ) -> TestingEngineState { - let fast_market_order_address = config.fast_market_order_address.unwrap_or( - current_state - .fast_market_order() - .expect("Fast market order is not created") - .fast_market_order_address, - ); - let router_endpoints = current_state - .router_endpoints() - .expect("Router endpoints are not created"); - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let auction_config_address = current_state - .auction_config_address() - .expect("Auction config address not found"); - let custodian_address = current_state - .custodian_address() - .expect("Custodian address not found"); - let auction_accounts = AuctionAccounts::new( - None, - solver.clone(), - auction_config_address, - &router_endpoints.endpoints, - custodian_address, - self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, - ); - let fast_vaa_data = current_state - .get_first_test_vaa_pair() - .fast_transfer_vaa - .get_vaa_data(); - let place_initial_offer_shim_fixture = - shimful::shims_make_offer::place_initial_offer_fallback( - &self.testing_context, - &payer_signer, - &fast_vaa_data, - solver, - &fast_market_order_address, - &auction_accounts, - config.offer_price, - config.expected_error.as_ref(), - ) - .await; - if config.expected_error.is_none() { - let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); - let active_auction_state = initial_offer_placed_state - .auction_state - .get_active_auction() - .unwrap(); - active_auction_state - .verify_auction(&self.testing_context) - .await; - return TestingEngineState::InitialOfferPlaced { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: initial_offer_placed_state.auction_state, - auction_accounts, - }; - } - current_state.clone() - } - - async fn execute_order_shim( - &self, - current_state: &TestingEngineState, - config: &ExecuteOrderInstructionConfig, - ) -> TestingEngineState { - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - - // TODO: Change to get auction accounts from current state - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - let fast_market_order_address = config.fast_market_order_address.unwrap_or( - current_state - .fast_market_order() - .expect("Fast market order is not created") - .fast_market_order_address, - ); - let active_auction_state = current_state - .auction_state() - .get_active_auction() - .expect("Active auction not found"); - let result = shimful::shims_execute_order::execute_order_fallback_test( - &self.testing_context, - &auction_accounts, - &fast_market_order_address, - &active_auction_state, - solver, - config.expected_error.as_ref(), - ) - .await; - if config.expected_error.is_none() { - let order_executed_fallback_fixture = result.unwrap(); - let order_executed_state = OrderExecutedState { - cctp_message: order_executed_fallback_fixture.cctp_message, - post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), - post_message_message: Some(order_executed_fallback_fixture.post_message_message), - }; - TestingEngineState::OrderExecuted { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_executed: order_executed_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } - } - - async fn execute_order_shimless( - &self, - current_state: &TestingEngineState, - config: &ExecuteOrderInstructionConfig, - ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let auction_config_address = current_state - .auction_config_address() - .expect("Auction config address not found"); - let router_endpoints = current_state - .router_endpoints() - .expect("Router endpoints are not created"); - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - let custodian_address = current_state - .custodian_address() - .expect("Custodian address not found"); - let auction_accounts = AuctionAccounts::new( - Some( - current_state - .get_first_test_vaa_pair() - .fast_transfer_vaa - .get_vaa_pubkey(), - ), - solver.clone(), - auction_config_address, - &router_endpoints.endpoints, - custodian_address, - self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, - ); - let result = shimless::execute_order::execute_order_shimless_test( - &self.testing_context, - &auction_accounts, - current_state.auction_state(), - &payer_signer, - config.expected_error.as_ref(), - ) - .await; - if config.expected_error.is_none() { - let execute_order_fixture = result.unwrap(); - let order_executed_state = OrderExecutedState { - cctp_message: execute_order_fixture.cctp_message, - post_message_sequence: None, - post_message_message: None, - }; - TestingEngineState::OrderExecuted { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_executed: order_executed_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } - } - - async fn prepare_order_shim( - &self, - current_state: &TestingEngineState, - config: &PrepareOrderInstructionConfig, - ) -> TestingEngineState { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - - let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); - let deposit_vaa_data = deposit_vaa.get_vaa_data(); - let deposit = deposit_vaa - .payload_deserialized - .clone() - .unwrap() - .get_deposit() - .unwrap(); - - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - - let result = shimful::shims_prepare_order_response::prepare_order_response_test( - &self.testing_context, - &payer_signer, - &deposit_vaa_data, - current_state, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, - &deposit, - config.expected_error.as_ref(), - ) - .await; - if config.expected_error.is_none() { - let prepare_order_response_fixture = result.unwrap(); - let order_prepared_state = OrderPreparedState { - prepared_order_address: prepare_order_response_fixture.prepared_order_response, - prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - }; - TestingEngineState::OrderPrepared { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_prepared: order_prepared_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } - } - - async fn prepare_order_shimless( - &self, - current_state: &TestingEngineState, - config: &PrepareOrderInstructionConfig, - ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - let solver_token_account = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index") - .token_account_address() - .expect("Token account does not exist for solver at index"); - let result = shimless::prepare_order_response::prepare_order_response( - &self.testing_context, - &payer_signer, - current_state, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, - &solver_token_account, - config.expected_error.as_ref(), - config.expected_log_message.as_ref(), - ) - .await; - if config.expected_error.is_none() { - let prepare_order_response_fixture = result.unwrap(); - let order_prepared_state = OrderPreparedState { - prepared_order_address: prepare_order_response_fixture.prepared_order_response, - prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - }; - TestingEngineState::OrderPrepared { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_prepared: order_prepared_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } - } - - async fn settle_auction( - &self, - current_state: &TestingEngineState, - config: &SettleAuctionInstructionConfig, - ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - let order_prepared_state = current_state - .order_prepared() - .expect("Order prepared not found"); - let prepared_custody_token = order_prepared_state.prepared_custody_token; - let prepared_order_response = order_prepared_state.prepared_order_address; - let auction_state = shimless::settle_auction::settle_auction_complete( - &self.testing_context, - &payer_signer, - current_state.auction_state(), - &prepared_order_response, - &prepared_custody_token, - &self.testing_context.get_matching_engine_program_id(), - config.expected_error.as_ref(), - ) - .await; - match auction_state { - AuctionState::Settled => TestingEngineState::AuctionSettled { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - auction_state: current_state.auction_state().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - order_prepared: order_prepared_state.clone(), - auction_accounts: current_state.auction_accounts().cloned(), - }, - _ => current_state.clone(), - } - } } From 240752593253205a39b2f60579ee5f59a66f5ff0 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 12:26:05 +0000 Subject: [PATCH 029/112] testing readme added --- .../programs/matching-engine/tests/README.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 solana/programs/matching-engine/tests/README.md diff --git a/solana/programs/matching-engine/tests/README.md b/solana/programs/matching-engine/tests/README.md new file mode 100644 index 000000000..2de57f620 --- /dev/null +++ b/solana/programs/matching-engine/tests/README.md @@ -0,0 +1,38 @@ +# Matching Engine Tests + +## How to read the tests + +Each test is found in the `integration_tests.rs` file. + +Each test is a function that is annotated with `#[tokio::test]`. + +Each test is a test for a specific scenario, and uses the `TestingEngine` to execute a series of instruction triggers. + +The `TestingEngine` is initialised with a `TestingContext`. The `TestingContext` holds the solana program test context, the actors, the transfer direction, created vaas, as well as some constants. + +The `TestingEngine` is used to execute the instruction triggers in the order they are provided. See the `testing_engine/engine.rs` file for more details. + + +## Integration Tests + +### Initialize program + +What is expected: +- Program is initialised +- Router endpoints are created + +### Create fast market order + +What is expected: +- Fast market order account is created +- Guardian set is created +- Fast market order is initialised + +### Close fast market order + +What is expected: +- Fast market order account is closed +- Guardian set is closed +- Close account refund recipient is sent usdc + + From d934be78699babd2f47339f558deeb35981657bc Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 14:06:11 +0000 Subject: [PATCH 030/112] more comments --- .../src/fallback/processor/mod.rs | 6 +- .../fallback/processor/place_initial_offer.rs | 3 + .../fallback/processor/process_instruction.rs | 81 ++- .../programs/matching-engine/tests/README.md | 7 +- .../tests/integration_tests.rs | 374 ++++++++++++- .../tests/shimful/fast_market_order_shim.rs | 111 +++- .../matching-engine/tests/shimful/mod.rs | 2 +- .../tests/shimful/post_message.rs | 157 +++--- .../tests/shimful/shims_make_offer.rs | 31 +- .../tests/shimful/verify_shim.rs | 210 ++----- .../matching-engine/tests/shimless/mod.rs | 2 +- .../tests/testing_engine/engine.rs | 515 +++++++++++++++++- .../matching-engine/tests/utils/mod.rs | 2 +- 13 files changed, 1181 insertions(+), 320 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 259faa1e8..f9e956cb6 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,10 +1,10 @@ pub mod process_instruction; pub use process_instruction::*; -pub mod burn_and_post; +// pub mod burn_and_post; pub mod close_fast_market_order; -pub mod execute_order; +// pub mod execute_order; pub mod initialise_fast_market_order; pub mod place_initial_offer; -pub mod prepare_order_response; +// pub mod prepare_order_response; pub mod helpers; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 2099b1587..e61119292 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -98,6 +98,9 @@ impl PlaceInitialOfferCctpShim<'_> { } } +/// VaaMessageBodyHeader for the digest calculation +/// +/// This is the header of the vaa message body. It is used to calculate the digest of the fast market order. #[derive(Debug)] pub struct VaaMessageBodyHeader { pub consistency_level: u8, diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index ccafb3f8a..6644334dc 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -1,12 +1,11 @@ use super::close_fast_market_order::close_fast_market_order; -use super::execute_order::handle_execute_order_shim; +// use super::execute_order::handle_execute_order_shim; use super::initialise_fast_market_order::{ initialise_fast_market_order, InitialiseFastMarketOrderData, }; -use super::place_initial_offer::place_initial_offer_cctp_shim; -use super::place_initial_offer::PlaceInitialOfferCctpShimData; -use super::prepare_order_response::prepare_order_response_cctp_shim; -use super::prepare_order_response::PrepareOrderResponseCctpShimData; +use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; +// use super::prepare_order_response::prepare_order_response_cctp_shim; +// use super::prepare_order_response::PrepareOrderResponseCctpShimData; use crate::ID; use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; @@ -28,8 +27,8 @@ pub enum FallbackMatchingEngineInstruction<'ix> { InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), CloseFastMarketOrder, PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), - ExecuteOrderCctpShim, - PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), + // ExecuteOrderCctpShim, + // PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } pub fn process_instruction( @@ -51,13 +50,12 @@ pub fn process_instruction( } FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, &data) - } - FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { - handle_execute_order_shim(accounts) - } - FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { - prepare_order_response_cctp_shim(accounts, data) - } + } // FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { + // handle_execute_order_shim(accounts) + // } + // FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { + // prepare_order_response_cctp_shim(accounts, data) + // } } } @@ -73,20 +71,21 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { &PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), )) } - FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { - Some(Self::ExecuteOrderCctpShim) - } + FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => Some( Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..])), ), FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) } - FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { - Some(Self::PrepareOrderResponseCctpShim( - PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), - )) - } + // FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { + // Some(Self::ExecuteOrderCctpShim) + // } + // FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { + // Some(Self::PrepareOrderResponseCctpShim( + // PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + // )) + // } _ => None, } } @@ -113,17 +112,17 @@ impl FallbackMatchingEngineInstruction<'_> { out } - Self::ExecuteOrderCctpShim => { - let total_capacity = 8; // 8 for the selector (no data) + // Self::ExecuteOrderCctpShim => { + // let total_capacity = 8; // 8 for the selector (no data) - let mut out = Vec::with_capacity(total_capacity); + // let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice( - &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, - ); + // out.extend_from_slice( + // &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, + // ); - out - } + // out + // } Self::InitialiseFastMarketOrder(data) => { let data_slice = bytemuck::bytes_of(*data); let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length @@ -147,21 +146,19 @@ impl FallbackMatchingEngineInstruction<'_> { ); out - } - - Self::PrepareOrderResponseCctpShim(data) => { - let data_slice = data.to_bytes(); - let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + } // Self::PrepareOrderResponseCctpShim(data) => { + // let data_slice = data.to_bytes(); + // let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length - let mut out = Vec::with_capacity(total_capacity); + // let mut out = Vec::with_capacity(total_capacity); - out.extend_from_slice( - &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, - ); - out.extend_from_slice(&data_slice); + // out.extend_from_slice( + // &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, + // ); + // out.extend_from_slice(&data_slice); - out - } + // out + // } } } } diff --git a/solana/programs/matching-engine/tests/README.md b/solana/programs/matching-engine/tests/README.md index 2de57f620..4b9378673 100644 --- a/solana/programs/matching-engine/tests/README.md +++ b/solana/programs/matching-engine/tests/README.md @@ -12,7 +12,6 @@ The `TestingEngine` is initialised with a `TestingContext`. The `TestingContext` The `TestingEngine` is used to execute the instruction triggers in the order they are provided. See the `testing_engine/engine.rs` file for more details. - ## Integration Tests ### Initialize program @@ -21,6 +20,12 @@ What is expected: - Program is initialised - Router endpoints are created + +### Create CCTP router endpoints + +What is expected: +- CCTP router endpoints are created + ### Create fast market order What is expected: diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index fb2f835eb..ddb06f6a8 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -1,5 +1,6 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; +use matching_engine::error::MatchingEngineError; use matching_engine::ID as PROGRAM_ID; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -8,10 +9,14 @@ mod shimful; mod shimless; mod testing_engine; mod utils; -use crate::testing_engine::config::InitializeInstructionConfig; +use crate::testing_engine::config::{ + ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, + PlaceInitialOfferInstructionConfig, +}; use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; use shimful::post_message::set_up_post_message_transaction_test; use shimless::initialize::{initialize_program, AuctionParametersConfig}; +use solana_sdk::transaction::TransactionError; use utils::router::add_local_router_endpoint_ix; use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; use utils::vaa::VaaArgs; @@ -240,3 +245,370 @@ pub async fn test_approve_usdc() { TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); assert!(solver_token_account.delegate.is_some()); } + +#[tokio::test] +// Testing a initial offer from arbitrum to ethereum +// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction +pub async fn test_place_initial_offer_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig { + solver_index: 1, + ..ImproveOfferInstructionConfig::default() + }), + ]; + + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_place_initial_offer_shim_blocks_non_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + solver_index: 0, + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + solver_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_place_initial_offer_non_shim_blocks_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + solver_index: 0, + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + solver_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +// #[tokio::test] +// // Testing an execute order from arbitrum to ethereum +// // TODO: Flesh out this test to see if the message was posted correctly +// pub async fn test_execute_order_fallback() { +// let transfer_direction = TransferDirection::FromArbitrumToEthereum; +// let vaa_args = VaaArgs { +// post_vaa: false, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// #[tokio::test] +// pub async fn test_execute_order_shimless() { +// let transfer_direction = TransferDirection::FromArbitrumToEthereum; +// let vaa_args = VaaArgs { +// post_vaa: true, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } +// pub async fn test_execute_order_fallback_blocks_shimless() { +// let transfer_direction = TransferDirection::FromArbitrumToEthereum; +// let vaa_args = VaaArgs { +// post_vaa: true, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { +// expected_error: Some(ExpectedError { +// instruction_index: 0, +// error_code: MatchingEngineError::AccountAlreadyInitialized.into(), +// error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), +// }), +// ..ExecuteOrderInstructionConfig::default() +// }), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// // From ethereum to arbitrum +// #[tokio::test] +// pub async fn test_prepare_order_shim_fallback() { +// let transfer_direction = TransferDirection::FromEthereumToArbitrum; +// let vaa_args = VaaArgs { +// post_vaa: false, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// // Prepare order response from ethereum to arbitrum (shimless) +// #[tokio::test] +// pub async fn test_prepare_order_shimless() { +// let transfer_direction = TransferDirection::FromEthereumToArbitrum; +// let vaa_args = VaaArgs { +// post_vaa: true, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// #[tokio::test] +// pub async fn test_prepare_order_response_shimful_blocks_shimless() { +// let transfer_direction = TransferDirection::FromEthereumToArbitrum; +// let vaa_args = VaaArgs { +// post_vaa: true, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), +// //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them +// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { +// // expected_log_message: Some("Already prepared".to_string()), +// ..PrepareOrderInstructionConfig::default() +// }), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// #[tokio::test] +// pub async fn test_prepare_order_response_shimless_blocks_shimful() { +// let transfer_direction = TransferDirection::FromEthereumToArbitrum; +// let vaa_args = VaaArgs { +// post_vaa: true, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), +// // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above +// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { +// expected_error: Some(ExpectedError { +// instruction_index: 0, +// error_code: 0, +// error_string: TransactionError::AccountInUse.to_string(), +// }), +// ..PrepareOrderInstructionConfig::default() +// }), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } + +// #[tokio::test] +// pub async fn test_settle_auction_complete() { +// let transfer_direction = TransferDirection::FromEthereumToArbitrum; +// let vaa_args = VaaArgs { +// post_vaa: false, +// ..VaaArgs::default() +// }; +// let testing_context = setup_environment( +// ShimMode::VerifyAndPostSignature, +// transfer_direction, +// Some(vaa_args), +// ) +// .await; + +// let testing_engine = TestingEngine::new(testing_context).await; +// let instruction_triggers = vec![ +// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +// InstructionTrigger::CreateCctpRouterEndpoints( +// CreateCctpRouterEndpointsInstructionConfig::default(), +// ), +// InstructionTrigger::InitializeFastMarketOrderShim( +// InitializeFastMarketOrderShimInstructionConfig::default(), +// ), +// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), +// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), +// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), +// InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), +// ]; +// testing_engine.execute(instruction_triggers).await; +// } diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs index 4a0f578d4..b4c8f19aa 100644 --- a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs @@ -15,11 +15,77 @@ use matching_engine::fallback::initialise_fast_market_order::{ }; use matching_engine::state::FastMarketOrder as FastMarketOrderState; +use solana_sdk::transaction::VersionedTransaction; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; use std::rc::Rc; use wormhole_io::TypePrefixedPayload; -pub fn initialise_fast_market_order_fallback_instruction( +/// Initialise the fast market order account +/// +/// This function initialises the fast market order account +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `payer_signer` - The payer signer keypair +/// * `fast_market_order` - The fast market order state +/// * `guardian_set_pubkey` - The guardian set pubkey +/// * `guardian_signatures_pubkey` - The guardian signatures pubkey +/// * `guardian_set_bump` - The guardian set bump +/// * `expected_error` - The expected error +/// +/// # Asserts +/// +/// * The expected error, if any, is reached when executing the instruction +pub async fn initialise_fast_market_order_fallback( + testing_context: &TestingContext, + payer_signer: &Rc, + fast_market_order: FastMarketOrderState, + guardian_set_pubkey: Pubkey, + guardian_signatures_pubkey: Pubkey, + guardian_set_bump: u8, + expected_error: Option<&ExpectedError>, +) { + let program_id = &testing_context.get_matching_engine_program_id(); + let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + payer_signer, + program_id, + fast_market_order, + guardian_set_pubkey, + guardian_signatures_pubkey, + guardian_set_bump, + ); + let recent_blockhash = testing_context.test_context.borrow().last_blockhash; + let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( + &[initialise_fast_market_order_ix], + Some(&payer_signer.pubkey()), + &[payer_signer], + recent_blockhash, + ); + let versioned_transaction = VersionedTransaction::try_from(transaction) + .expect("Failed to convert transaction to versioned transaction"); + testing_context + .execute_and_verify_transaction(versioned_transaction, expected_error) + .await; +} + +/// Creates the initialise fast market order fallback instruction +/// +/// This function creates the initialise fast market order fallback instruction +/// +/// # Arguments +/// +/// * `payer_signer` - The payer signer keypair +/// * `program_id` - The program id +/// * `fast_market_order` - The fast market order state +/// * `guardian_set_pubkey` - The guardian set pubkey +/// * `guardian_signatures_pubkey` - The guardian signatures pubkey +/// * `guardian_set_bump` - The guardian set bump +/// +/// # Returns +/// +/// * `Instruction` - The initialise fast market order fallback instruction +fn initialise_fast_market_order_fallback_instruction( payer_signer: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, @@ -54,13 +120,27 @@ pub fn initialise_fast_market_order_fallback_instruction( .instruction() } +/// Close the fast market order account +/// +/// This function closes the fast market order account +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `refund_recipient_keypair` - The refund recipient keypair that will receive the refund after closing the fast market order account +/// * `fast_market_order_address` - The fast market order account address +/// * `expected_error` - The expected error +/// +/// # Asserts +/// +/// * The expected error, if any, is reached when executing the instruction pub async fn close_fast_market_order_fallback( testing_context: &TestingContext, refund_recipient_keypair: &Rc, - program_id: &Pubkey, fast_market_order_address: &Pubkey, expected_error: Option<&ExpectedError>, ) { + let program_id = &testing_context.get_matching_engine_program_id(); let test_ctx = &testing_context.test_context; let recent_blockhash = test_ctx .borrow_mut() @@ -87,21 +167,22 @@ pub async fn close_fast_market_order_fallback( .await; } +/// Create the fast market order state from the vaa data +/// +/// This function creates the fast market order state from the vaa data +/// +/// # Arguments +/// +/// * `vaa_data` - The vaa data +/// * `close_account_refund_recipient` - The close account refund recipient +/// +/// # Returns +/// +/// * `fast_market_order_state` - The fast market order state pub fn create_fast_market_order_state_from_vaa_data( vaa_data: &utils::vaa::PostedVaaData, close_account_refund_recipient: Pubkey, -) -> (FastMarketOrderState, utils::vaa::PostedVaaData) { - let vaa_data = utils::vaa::PostedVaaData { - consistency_level: vaa_data.consistency_level, - vaa_time: vaa_data.vaa_time, - sequence: vaa_data.sequence, - emitter_chain: vaa_data.emitter_chain, - emitter_address: vaa_data.emitter_address, - payload: vaa_data.payload.clone(), - nonce: vaa_data.nonce, - vaa_signature_account: vaa_data.vaa_signature_account, - submission_time: 0, - }; +) -> FastMarketOrderState { let vaa_message = matching_engine::fallback::place_initial_offer::VaaMessageBodyHeader::new( vaa_data.consistency_level, vaa_data.vaa_time, @@ -152,5 +233,5 @@ pub fn create_fast_market_order_state_from_vaa_data( vaa_data.digest().as_ref() ); - (fast_market_order, vaa_data) + fast_market_order } diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs index 4718fadfa..d3b7aa41e 100644 --- a/solana/programs/matching-engine/tests/shimful/mod.rs +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -1,6 +1,6 @@ pub mod fast_market_order_shim; pub mod post_message; // pub mod shims_execute_order; -// pub mod shims_make_offer; +pub mod shims_make_offer; // pub mod shims_prepare_order_response; pub mod verify_shim; diff --git a/solana/programs/matching-engine/tests/shimful/post_message.rs b/solana/programs/matching-engine/tests/shimful/post_message.rs index 55d3ec3c8..15363a721 100644 --- a/solana/programs/matching-engine/tests/shimful/post_message.rs +++ b/solana/programs/matching-engine/tests/shimful/post_message.rs @@ -14,16 +14,102 @@ use wormhole_svm_definitions::{ }; use wormhole_svm_shim::post_message; +/// Bump costs +/// +/// This struct contains the bump costs for the message and sequence +/// +/// # Fields +/// +/// * `message` - The bump cost for the message +/// * `sequence` - The bump cost for the sequence pub struct BumpCosts { pub message: u64, pub sequence: u64, } -pub fn bump_cu_cost(bump: u8) -> u64 { - 1_500 * (255 - u64::from(bump)) +impl BumpCosts { + fn from_message_and_sequence_bump(message_bump: u8, sequence_bump: u8) -> Self { + Self { + message: Self::bump_cu_cost(message_bump), + sequence: Self::bump_cu_cost(sequence_bump), + } + } + + fn bump_cu_cost(bump: u8) -> u64 { + 1_500 * (255 - u64::from(bump)) + } +} + +/// Set up post message transaction test +/// +/// This function executes a post message transaction and asserts the correct logs are emitted +/// +/// # Arguments +/// +/// * `test_ctx` - The test context +/// * `payer_signer` - The payer signer keypair +/// * `emitter_signer` - The emitter signer keypair +pub async fn set_up_post_message_transaction_test( + test_ctx: &Rc>, + payer_signer: &Rc, + emitter_signer: &Rc, +) { + let recent_blockhash = test_ctx + .borrow_mut() + .get_new_latest_blockhash() + .await + .expect("Could not get last blockhash"); + let (transaction, _bump_costs) = set_up_post_message_transaction( + b"All your base are belong to us", + &payer_signer.clone().to_owned(), + &emitter_signer.clone().to_owned(), + recent_blockhash, + ); + let details = { + let out = test_ctx + .borrow_mut() + .banks_client + .simulate_transaction(transaction) + .await + .unwrap(); + assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); + out.simulation_details.unwrap() + }; + let logs = details.logs; + let is_core_bridge_cpi_log = + |line: &String| line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()); + // CPI to Core Bridge. + assert_eq!( + logs.iter() + .filter(|line| { + line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) + }) + .count(), + 1 + ); + assert_eq!( + logs.iter() + .filter(|line| { line.contains("Program log: Sequence: 0") }) + .count(), + 1 + ); + let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); + + // Self CPI. + assert_eq!( + logs.iter() + .skip(core_bridge_log_index) + .filter(|line| { + line.contains( + format!("Program {} invoke [2]", WORMHOLE_POST_MESSAGE_SHIM_PID).as_str(), + ) + }) + .count(), + 1 + ); } -pub fn set_up_post_message_transaction( +fn set_up_post_message_transaction( payload: &[u8], payer_signer: &Keypair, emitter_signer: &Keypair, @@ -84,69 +170,6 @@ pub fn set_up_post_message_transaction( ( transaction, - BumpCosts { - message: bump_cu_cost(message_bump), - sequence: bump_cu_cost(sequence_bump), - }, + BumpCosts::from_message_and_sequence_bump(message_bump, sequence_bump), ) } - -pub async fn set_up_post_message_transaction_test( - test_ctx: &Rc>, - payer_signer: &Rc, - emitter_signer: &Rc, -) { - let recent_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Could not get last blockhash"); - let (transaction, _bump_costs) = set_up_post_message_transaction( - b"All your base are belong to us", - &payer_signer.clone().to_owned(), - &emitter_signer.clone().to_owned(), - recent_blockhash, - ); - let details = { - let out = test_ctx - .borrow_mut() - .banks_client - .simulate_transaction(transaction) - .await - .unwrap(); - assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); - out.simulation_details.unwrap() - }; - let logs = details.logs; - let is_core_bridge_cpi_log = - |line: &String| line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()); - // CPI to Core Bridge. - assert_eq!( - logs.iter() - .filter(|line| { - line.contains(format!("Program {} invoke [2]", CORE_BRIDGE_PID).as_str()) - }) - .count(), - 1 - ); - assert_eq!( - logs.iter() - .filter(|line| { line.contains("Program log: Sequence: 0") }) - .count(), - 1 - ); - let core_bridge_log_index = logs.iter().position(is_core_bridge_cpi_log).unwrap(); - - // Self CPI. - assert_eq!( - logs.iter() - .skip(core_bridge_log_index) - .filter(|line| { - line.contains( - format!("Program {} invoke [2]", WORMHOLE_POST_MESSAGE_SHIM_PID).as_str(), - ) - }) - .count(), - 1 - ); -} diff --git a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs index 6c4807c74..ec38512e8 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs @@ -16,6 +16,26 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction use std::rc::Rc; /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `payer_signer` - The payer signer +/// * `vaa_data` - The vaa data (not posted) +/// * `solver` - The solver actor that will place the initial offer +/// * `fast_market_order_account` - The fast market order account pubkey created by the create fast market order shim instruction +/// * `auction_accounts` - The auction accounts (see utils/auction.rs) +/// * `offer_price` - The offer price in the units of the offer token +/// * `expected_error` - The expected error (None if no error is expected) +/// +/// # Returns +/// +/// * `Option` - An auction state with the initial offer placed. None if an error is expected. +/// +/// # Asserts +/// +/// * The expected error is reached +/// * If successful, the solver's USDC balance should decrease by the offer price pub async fn place_initial_offer_fallback( testing_context: &TestingContext, payer_signer: &Rc, @@ -28,8 +48,7 @@ pub async fn place_initial_offer_fallback( ) -> Option { let program_id = testing_context.get_matching_engine_program_id(); let test_ctx = &testing_context.test_context; - let (fast_market_order, vaa_data) = - create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); + let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.digest()], @@ -60,8 +79,7 @@ pub async fn place_initial_offer_fallback( .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) .await; - let solver_usdc_balance = solver.get_balance(test_ctx).await; - println!("Solver USDC balance: {:?}", solver_usdc_balance); + let solver_usdc_balance_before = solver.get_balance(test_ctx).await; let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( offer_price, @@ -105,6 +123,11 @@ pub async fn place_initial_offer_fallback( .execute_and_verify_transaction(transaction, expected_error) .await; if expected_error.is_none() { + let solver_usdc_balance_after = solver.get_balance(test_ctx).await; + assert!( + solver_usdc_balance_after < solver_usdc_balance_before, + "Solver USDC balance should have decreased" + ); let new_active_auction_state = utils::auction::ActiveAuctionState { auction_address, auction_custody_token_address, diff --git a/solana/programs/matching-engine/tests/shimful/verify_shim.rs b/solana/programs/matching-engine/tests/shimful/verify_shim.rs index 2f68c31fe..ff49376fa 100644 --- a/solana/programs/matching-engine/tests/shimful/verify_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/verify_shim.rs @@ -1,7 +1,6 @@ use crate::utils; use crate::utils::constants::*; use anchor_lang::prelude::*; -use base64::Engine; use solana_program_test::ProgramTestContext; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, @@ -13,10 +12,24 @@ use solana_sdk::{ use std::cell::RefCell; use std::rc::Rc; use std::str::FromStr; -use wormhole_svm_definitions::borsh::GuardianSignatures; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use wormhole_svm_shim::verify_vaa; +/// Create guardian signatures for a given vaa data +/// +/// This also creates the account holding the signatures and posts the signatures to the guardian signatures account +/// +/// # Arguments +/// +/// * `test_ctx` - The test context +/// * `payer_signer` - The payer signer +/// * `vaa_data` - The vaa data +/// * `wormhole_program_id` - The wormhole program id +/// * `guardian_signature_signer` - The guardian signature signer keypair. If None, a new keypair is created. +/// +/// # Returns +/// +/// * `(guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump)` - The guardian set pubkey, the guardian signatures pubkey and the guardian set bump pub async fn create_guardian_signatures( test_ctx: &Rc>, payer_signer: &Rc, @@ -50,7 +63,22 @@ pub async fn create_guardian_signatures( ) } -pub async fn add_guardian_signatures_account( +/// Add a guardian signatures account +/// +/// This creates a new guardian signatures account and posts the signatures to it +/// +/// # Arguments +/// +/// * `test_ctx` - The test context +/// * `payer_signer` - The payer signer +/// * `signatures_signer` - The signatures signer keypair. If None, a new keypair is created. +/// * `guardian_signatures` - The guardian signatures +/// * `guardian_set_index` - The guardian set index +/// +/// # Returns +/// +/// * `guardian_signatures_pubkey` - The guardian signatures pubkey +async fn add_guardian_signatures_account( test_ctx: &Rc>, payer_signer: &Rc, signatures_signer: &Rc, @@ -80,6 +108,22 @@ pub async fn add_guardian_signatures_account( Ok(signatures_signer.pubkey()) } +/// Post signatures transaction +/// +/// Creates the transaction to post the signatures to the guardian signatures account +/// +/// # Arguments +/// +/// * `payer_signer` - The payer signer +/// * `guardian_signatures_signer` - The guardian signatures signer +/// * `guardian_set_index` - The guardian set index +/// * `total_signatures` - The total signatures +/// * `guardian_signatures_vec` - The guardian signatures +/// * `recent_blockhash` - The recent blockhash +/// +/// # Returns +/// +/// * `VersionedTransaction` - The versioned transaction that can be executed to post the signatures fn post_signatures_transaction( payer_signer: &Rc, guardian_signatures_signer: &Rc, @@ -121,163 +165,3 @@ fn post_signatures_transaction( ) .unwrap() } - -#[allow(dead_code)] -/// Post signatures before the auction is created. -pub async fn set_up_verify_shims_test( - test_ctx: &Rc>, - payer_signer: &Rc, -) -> Result { - let guardian_signatures_signer = Rc::new(Keypair::new()); - let (transaction, decoded_vaa) = - set_up_verify_shims_transaction(test_ctx, payer_signer, &guardian_signatures_signer); - - let _details = { - let out = test_ctx - .borrow_mut() - .banks_client - .simulate_transaction(transaction.clone()) - .await - .unwrap(); - assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); - assert_eq!( - out.simulation_details.clone().unwrap().units_consumed, - // 13_355 - 3_337 - ); - out.simulation_details.unwrap() - }; - - { - let out = test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await; - assert!(out.is_ok()); - out.unwrap(); - }; - - // Check guardian signatures account after processing the transaction. - let guardian_signatures_info = test_ctx - .borrow_mut() - .banks_client - .get_account(guardian_signatures_signer.pubkey()) - .await - .unwrap() - .unwrap(); - - let account_data = &guardian_signatures_info.data; - let (expected_length, expected_guardian_signatures_data) = - generate_expected_guardian_signatures_info( - &payer_signer.pubkey(), - decoded_vaa.total_signatures, - decoded_vaa.guardian_set_index, - decoded_vaa.guardian_signatures, - ); - - assert_eq!(account_data.len(), expected_length); - assert_eq!( - wormhole_svm_definitions::borsh::deserialize_with_discriminator::< - wormhole_svm_definitions::borsh::GuardianSignatures, - >(&account_data[..]) - .unwrap(), - expected_guardian_signatures_data - ); - Ok(guardian_signatures_signer.pubkey()) -} - -#[allow(dead_code)] -fn set_up_verify_shims_transaction( - test_ctx: &Rc>, - payer_signer: &Rc, - guardian_signatures_signer: &Rc, -) -> (VersionedTransaction, DecodedVaa) { - const VAA: &str = "AQAAAAQNAL1qji7v9KnngyX0VxK+3fCMVscWTLoYX8L48NWquq2WGrcHd4H0wYc0KF4ZOWjLD2okXoBjGQIDJzx4qIrbSzQBAQq69h+neXGb58VfhZgraPVCxJmnTj8JIDq5jqi3Qav1e+IW51mIJlOhSAdCRbEyQLzf6Z3C19WJJqSyt/z1XF0AAvFgDHkseyMZTE5vQjflu4tc5OLPJe2VYCxTJT15LA02YPrWgOM6HhfUhXDhFoG5AI/s2ApjK8jaqi7LGJILAUMBA6cp4vfko8hYyRvogqQWsdk9e20g0O6s60h4ewweapXCQHerQpoJYdDxlCehN4fuYnuudEhW+6FaXLjwNJBdqsoABDg9qXjXB47nBVCZAGns2eosVqpjkyDaCfo/p1x8AEjBA80CyC1/QlbG9L4zlnnDIfZWylsf3keJqx28+fZNC5oABi6XegfozgE8JKqvZLvd7apDhrJ6Qv+fMiynaXASkafeVJOqgFOFbCMXdMKehD38JXvz3JrlnZ92E+I5xOJaDVgABzDSte4mxUMBMJB9UUgJBeAVsokFvK4DOfvh6G3CVqqDJplLwmjUqFB7fAgRfGcA8PWNStRc+YDZiG66YxPnptwACe84S31Kh9voz2xRk1THMpqHQ4fqE7DizXPNWz6Z6ebEXGcd7UP9PBXoNNvjkLWZJZOdbkZyZqztaIiAo4dgWUABCobiuQP92WjTxOZz0KhfWVJ3YBVfsXUwaVQH4/p6khX0HCEVHR9VHmjvrAAGDMdJGWW+zu8mFQc4gPU6m4PZ6swADO7voA5GWZZPiztz22pftwxKINGvOjCPlLpM1Y2+Vq6AQuez/mlUAmaL0NKgs+5VYcM1SGBz0TL3ABRhKQAhUEMADWmiMo0J1Qaj8gElb+9711ZjvAY663GIyG/E6EdPW+nPKJI9iZE180sLct+krHj0J7PlC9BjDiO2y149oCOJ6FgAEcaVkYK43EpN7XqxrdpanX6R6TaqECgZTjvtN3L6AP2ceQr8mJJraYq+qY8pTfFvPKEqmW9CBYvnA5gIMpX59WsAEjIL9Hdnx+zFY0qSPB1hB9AhqWeBP/QfJjqzqafsczaeCN/rWUf6iNBgXI050ywtEp8JQ36rCn8w6dRhUusn+MEAZ32XyAAAAAAAFczO6yk0j3G90i/+9DoqGcH1teF8XMpUEVKRIBgmcq3lAAAAAAAC/1wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6Q7dAAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAgAAAAAAAAAAAAAAAInNTEvk5b/1WVF+JawF1smtAdicABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; - let decoded_vaa = DecodedVaa::from(VAA); - let decoded_vaa_clone = decoded_vaa.clone(); - assert_eq!(decoded_vaa.total_signatures, 13); - let recent_blockhash = test_ctx.borrow().last_blockhash; - let guardian_signatures_vec: &Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]> = - &decoded_vaa.guardian_signatures; - ( - post_signatures_transaction( - payer_signer, - guardian_signatures_signer, - decoded_vaa.guardian_set_index, - decoded_vaa.total_signatures, - guardian_signatures_vec, - recent_blockhash, - ), - decoded_vaa_clone, - ) -} - -#[allow(dead_code)] -fn generate_expected_guardian_signatures_info( - payer: &Pubkey, - total_signatures: u8, - guardian_set_index: u32, - guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, -) -> ( - usize, // expected length - GuardianSignatures, -) { - let expected_length = { - 8 // discriminator - + 32 // refund recipient - + 4 // guardian set index - + 4 // guardian signatures length - + (total_signatures as usize) * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH - }; - - let guardian_signatures = GuardianSignatures { - refund_recipient: *payer, - guardian_set_index_be: guardian_set_index.to_be_bytes(), - guardian_signatures, - }; - - (expected_length, guardian_signatures) -} - -#[derive(Clone)] -#[allow(dead_code)] -struct DecodedVaa { - pub guardian_set_index: u32, - pub total_signatures: u8, - pub guardian_signatures: Vec<[u8; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]>, - pub body: Vec, -} - -impl From<&str> for DecodedVaa { - fn from(vaa: &str) -> Self { - let mut buf = base64::prelude::BASE64_STANDARD.decode(vaa).unwrap(); - let guardian_set_index = u32::from_be_bytes(buf[1..5].try_into().unwrap()); - let total_signatures = buf[5]; - - let body = buf - .drain( - (6 + total_signatures as usize - * wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH).., - ) - .collect(); - - let mut guardian_signatures = Vec::with_capacity(total_signatures as usize); - - for i in 0..usize::from(total_signatures) { - let offset = 6 + i * 66; - let mut signature = [0; wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH]; - signature.copy_from_slice( - &buf[offset..offset + wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH], - ); - guardian_signatures.push(signature); - } - - Self { - guardian_set_index, - total_signatures, - guardian_signatures, - body, - } - } -} diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs index 3eb39ba62..f594cdb9a 100644 --- a/solana/programs/matching-engine/tests/shimless/mod.rs +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -1,5 +1,5 @@ // pub mod execute_order; pub mod initialize; -// pub mod make_offer; +pub mod make_offer; // pub mod prepare_order_response; // pub mod settle_auction; diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 689eb6865..9d011031f 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -1,15 +1,21 @@ use matching_engine::state::FastMarketOrder; -use solana_sdk::transaction::VersionedTransaction; use super::{config::*, state::*}; use crate::shimful; use crate::shimful::fast_market_order_shim::{ - create_fast_market_order_state_from_vaa_data, initialise_fast_market_order_fallback_instruction, + create_fast_market_order_state_from_vaa_data, initialise_fast_market_order_fallback, }; use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; use crate::utils::vaa::TestVaaPairs; -use crate::utils::{router::create_all_router_endpoints_test, setup::TestingContext}; +use crate::utils::{ + auction::{ + AuctionAccounts, + // AuctionState + }, + router::create_all_router_endpoints_test, + setup::TestingContext, +}; use anchor_lang::prelude::*; #[allow(dead_code)] @@ -17,6 +23,14 @@ pub enum InstructionTrigger { InitializeProgram(InitializeInstructionConfig), CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig), InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), + PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), + PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), + ImproveOfferShimless(ImproveOfferInstructionConfig), + // ExecuteOrderShimless(ExecuteOrderInstructionConfig), + // ExecuteOrderShim(ExecuteOrderInstructionConfig), + // PrepareOrderShimless(PrepareOrderInstructionConfig), + // PrepareOrderShim(PrepareOrderInstructionConfig), + // SettleAuction(SettleAuctionInstructionConfig), CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } @@ -96,6 +110,30 @@ impl TestingEngine { self.close_fast_market_order_account(current_state, config) .await } + InstructionTrigger::PlaceInitialOfferShimless(config) => { + self.place_initial_offer_shimless(current_state, config) + .await + } + InstructionTrigger::PlaceInitialOfferShim(config) => { + self.place_initial_offer_shim(current_state, config).await + } + InstructionTrigger::ImproveOfferShimless(config) => { + self.improve_offer_shimless(current_state, config).await + } // InstructionTrigger::ExecuteOrderShim(config) => { + // self.execute_order_shim(current_state, config).await + // } + // InstructionTrigger::ExecuteOrderShimless(config) => { + // self.execute_order_shimless(current_state, config).await + // } + // InstructionTrigger::PrepareOrderShim(config) => { + // self.prepare_order_shim(current_state, config).await + // } + // InstructionTrigger::PrepareOrderShimless(config) => { + // self.prepare_order_shimless(current_state, config).await + // } + // InstructionTrigger::SettleAuction(config) => { + // self.settle_auction(current_state, config).await + // } } } @@ -195,7 +233,7 @@ impl TestingEngine { ) -> TestingEngineState { let first_test_vaa_pair = current_state.get_first_test_vaa_pair(); let fast_transfer_vaa = first_test_vaa_pair.fast_transfer_vaa.clone(); - let (fast_market_order, vaa_data) = create_fast_market_order_state_from_vaa_data( + let fast_market_order = create_fast_market_order_state_from_vaa_data( &fast_transfer_vaa.vaa_data, config .close_account_refund_recipient @@ -209,7 +247,7 @@ impl TestingEngine { create_guardian_signatures( &self.testing_context.test_context, &payer_signer, - &vaa_data, + &fast_transfer_vaa.vaa_data, &self.testing_context.get_wormhole_program_id(), None, ) @@ -224,27 +262,17 @@ impl TestingEngine { &self.testing_context.get_matching_engine_program_id(), ); - let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + initialise_fast_market_order_fallback( + &self.testing_context, &payer_signer, - &self.testing_context.get_matching_engine_program_id(), fast_market_order, guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump, - ); + config.expected_error.as_ref(), + ) + .await; - let recent_blockhash = self.testing_context.test_context.borrow().last_blockhash; - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( - &[initialise_fast_market_order_ix], - Some(&self.testing_context.testing_actors.owner.pubkey()), - &[&self.testing_context.testing_actors.owner.keypair()], - recent_blockhash, - ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - self.testing_context - .execute_and_verify_transaction(versioned_transaction, config.expected_error.as_ref()) - .await; if config.expected_error.is_none() { TestingEngineState::FastMarketOrderAccountCreated { base: current_state.base().clone(), @@ -285,7 +313,6 @@ impl TestingEngine { shimful::fast_market_order_shim::close_fast_market_order_fallback( &self.testing_context, &close_account_refund_recipient, - &self.testing_context.get_matching_engine_program_id(), &fast_market_order_account, config.expected_error.as_ref(), ) @@ -301,4 +328,450 @@ impl TestingEngine { auction_accounts: current_state.auction_accounts().cloned(), } } + async fn place_initial_offer_shimless( + &self, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + ) -> TestingEngineState { + assert!( + current_state.router_endpoints().is_some(), + "Router endpoints are not created" + ); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let solver = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index"); + let expected_error = config.expected_error.as_ref(); + let fast_vaa = ¤t_state + .base() + .vaas + .get(0) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); + let auction_config_address = current_state + .initialized() + .expect("Testing state is not initialized") + .auction_config_address; + let custodian_address = current_state + .initialized() + .expect("Testing state is not initialized") + .custodian_address; + let auction_accounts = AuctionAccounts::new( + Some(fast_vaa_pubkey), + solver.clone(), + auction_config_address, + ¤t_state + .router_endpoints() + .expect("Router endpoints are not created") + .endpoints, + custodian_address, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, + ); + let auction_state = shimless::make_offer::place_initial_offer_shimless( + &self.testing_context, + &auction_accounts, + fast_vaa, + config.offer_price, + &payer_signer, + self.testing_context.get_matching_engine_program_id(), + expected_error, + ) + .await; + if expected_error.is_none() { + auction_state + .get_active_auction() + .unwrap() + .verify_auction(&self.testing_context) + .await; + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state, + auction_accounts, + }; + } + current_state.clone() + } + + async fn improve_offer_shimless( + &self, + current_state: &TestingEngineState, + config: &ImproveOfferInstructionConfig, + ) -> TestingEngineState { + let expected_error = config.expected_error.as_ref(); + let solver = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index"); + let offer_price = config.offer_price; + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let new_auction_state = shimless::make_offer::improve_offer( + &self.testing_context, + self.testing_context.get_matching_engine_program_id(), + solver.clone(), + auction_config_address, + offer_price, + &payer_signer, + current_state.auction_state(), + expected_error, + ) + .await; + if expected_error.is_none() { + let auction_state = new_auction_state.unwrap(); + return TestingEngineState::OfferImproved { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state, + auction_accounts: current_state.auction_accounts().cloned(), + }; + } + current_state.clone() + } + + async fn place_initial_offer_shim( + &self, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + ) -> TestingEngineState { + let fast_market_order_address = config.fast_market_order_address.unwrap_or( + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address, + ); + let router_endpoints = current_state + .router_endpoints() + .expect("Router endpoints are not created"); + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); + let auction_accounts = AuctionAccounts::new( + None, + solver.clone(), + auction_config_address, + &router_endpoints.endpoints, + custodian_address, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, + ); + let fast_vaa_data = current_state + .get_first_test_vaa_pair() + .fast_transfer_vaa + .get_vaa_data(); + let place_initial_offer_shim_fixture = + shimful::shims_make_offer::place_initial_offer_fallback( + &self.testing_context, + &payer_signer, + &fast_vaa_data, + solver, + &fast_market_order_address, + &auction_accounts, + config.offer_price, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); + let active_auction_state = initial_offer_placed_state + .auction_state + .get_active_auction() + .unwrap(); + active_auction_state + .verify_auction(&self.testing_context) + .await; + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: initial_offer_placed_state.auction_state, + auction_accounts, + }; + } + current_state.clone() + } + + // async fn execute_order_shim( + // &self, + // current_state: &TestingEngineState, + // config: &ExecuteOrderInstructionConfig, + // ) -> TestingEngineState { + // let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + + // // TODO: Change to get auction accounts from current state + // let auction_accounts = current_state + // .auction_accounts() + // .expect("Auction accounts not found"); + // let fast_market_order_address = config.fast_market_order_address.unwrap_or( + // current_state + // .fast_market_order() + // .expect("Fast market order is not created") + // .fast_market_order_address, + // ); + // let active_auction_state = current_state + // .auction_state() + // .get_active_auction() + // .expect("Active auction not found"); + // let result = shimful::shims_execute_order::execute_order_fallback_test( + // &self.testing_context, + // &auction_accounts, + // &fast_market_order_address, + // &active_auction_state, + // solver, + // config.expected_error.as_ref(), + // ) + // .await; + // if config.expected_error.is_none() { + // let order_executed_fallback_fixture = result.unwrap(); + // let order_executed_state = OrderExecutedState { + // cctp_message: order_executed_fallback_fixture.cctp_message, + // post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), + // post_message_message: Some(order_executed_fallback_fixture.post_message_message), + // }; + // TestingEngineState::OrderExecuted { + // base: current_state.base().clone(), + // initialized: current_state.initialized().unwrap().clone(), + // router_endpoints: current_state.router_endpoints().unwrap().clone(), + // fast_market_order: current_state.fast_market_order().cloned(), + // auction_state: current_state.auction_state().clone(), + // order_executed: order_executed_state, + // auction_accounts: auction_accounts.clone(), + // } + // } else { + // current_state.clone() + // } + // } + + // async fn execute_order_shimless( + // &self, + // current_state: &TestingEngineState, + // config: &ExecuteOrderInstructionConfig, + // ) -> TestingEngineState { + // let payer_signer = config + // .payer_signer + // .clone() + // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + // let auction_config_address = current_state + // .auction_config_address() + // .expect("Auction config address not found"); + // let router_endpoints = current_state + // .router_endpoints() + // .expect("Router endpoints are not created"); + // let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + // let custodian_address = current_state + // .custodian_address() + // .expect("Custodian address not found"); + // let auction_accounts = AuctionAccounts::new( + // Some( + // current_state + // .get_first_test_vaa_pair() + // .fast_transfer_vaa + // .get_vaa_pubkey(), + // ), + // solver.clone(), + // auction_config_address, + // &router_endpoints.endpoints, + // custodian_address, + // self.testing_context.get_usdc_mint_address(), + // self.testing_context.testing_state.transfer_direction, + // ); + // let result = shimless::execute_order::execute_order_shimless_test( + // &self.testing_context, + // &auction_accounts, + // current_state.auction_state(), + // &payer_signer, + // config.expected_error.as_ref(), + // ) + // .await; + // if config.expected_error.is_none() { + // let execute_order_fixture = result.unwrap(); + // let order_executed_state = OrderExecutedState { + // cctp_message: execute_order_fixture.cctp_message, + // post_message_sequence: None, + // post_message_message: None, + // }; + // TestingEngineState::OrderExecuted { + // base: current_state.base().clone(), + // initialized: current_state.initialized().unwrap().clone(), + // router_endpoints: current_state.router_endpoints().unwrap().clone(), + // fast_market_order: current_state.fast_market_order().cloned(), + // auction_state: current_state.auction_state().clone(), + // order_executed: order_executed_state, + // auction_accounts: auction_accounts.clone(), + // } + // } else { + // current_state.clone() + // } + // } + + // async fn prepare_order_shim( + // &self, + // current_state: &TestingEngineState, + // config: &PrepareOrderInstructionConfig, + // ) -> TestingEngineState { + // let auction_accounts = current_state + // .auction_accounts() + // .expect("Auction accounts not found"); + + // let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); + // let deposit_vaa_data = deposit_vaa.get_vaa_data(); + // let deposit = deposit_vaa + // .payload_deserialized + // .clone() + // .unwrap() + // .get_deposit() + // .unwrap(); + + // let payer_signer = config + // .payer_signer + // .clone() + // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + + // let result = shimful::shims_prepare_order_response::prepare_order_response_test( + // &self.testing_context, + // &payer_signer, + // &deposit_vaa_data, + // current_state, + // &auction_accounts.to_router_endpoint, + // &auction_accounts.from_router_endpoint, + // &deposit, + // config.expected_error.as_ref(), + // ) + // .await; + // if config.expected_error.is_none() { + // let prepare_order_response_fixture = result.unwrap(); + // let order_prepared_state = OrderPreparedState { + // prepared_order_address: prepare_order_response_fixture.prepared_order_response, + // prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + // }; + // TestingEngineState::OrderPrepared { + // base: current_state.base().clone(), + // initialized: current_state.initialized().unwrap().clone(), + // router_endpoints: current_state.router_endpoints().unwrap().clone(), + // fast_market_order: current_state.fast_market_order().cloned(), + // auction_state: current_state.auction_state().clone(), + // order_prepared: order_prepared_state, + // auction_accounts: auction_accounts.clone(), + // } + // } else { + // current_state.clone() + // } + // } + + // async fn prepare_order_shimless( + // &self, + // current_state: &TestingEngineState, + // config: &PrepareOrderInstructionConfig, + // ) -> TestingEngineState { + // let payer_signer = config + // .payer_signer + // .clone() + // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + // let auction_accounts = current_state + // .auction_accounts() + // .expect("Auction accounts not found"); + // let solver_token_account = self + // .testing_context + // .testing_actors + // .solvers + // .get(config.solver_index) + // .expect("Solver not found at index") + // .token_account_address() + // .expect("Token account does not exist for solver at index"); + // let result = shimless::prepare_order_response::prepare_order_response( + // &self.testing_context, + // &payer_signer, + // current_state, + // &auction_accounts.to_router_endpoint, + // &auction_accounts.from_router_endpoint, + // &solver_token_account, + // config.expected_error.as_ref(), + // config.expected_log_message.as_ref(), + // ) + // .await; + // if config.expected_error.is_none() { + // let prepare_order_response_fixture = result.unwrap(); + // let order_prepared_state = OrderPreparedState { + // prepared_order_address: prepare_order_response_fixture.prepared_order_response, + // prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + // }; + // TestingEngineState::OrderPrepared { + // base: current_state.base().clone(), + // initialized: current_state.initialized().unwrap().clone(), + // router_endpoints: current_state.router_endpoints().unwrap().clone(), + // fast_market_order: current_state.fast_market_order().cloned(), + // auction_state: current_state.auction_state().clone(), + // order_prepared: order_prepared_state, + // auction_accounts: auction_accounts.clone(), + // } + // } else { + // current_state.clone() + // } + // } + + // async fn settle_auction( + // &self, + // current_state: &TestingEngineState, + // config: &SettleAuctionInstructionConfig, + // ) -> TestingEngineState { + // let payer_signer = config + // .payer_signer + // .clone() + // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + // let order_prepared_state = current_state + // .order_prepared() + // .expect("Order prepared not found"); + // let prepared_custody_token = order_prepared_state.prepared_custody_token; + // let prepared_order_response = order_prepared_state.prepared_order_address; + // let auction_state = shimless::settle_auction::settle_auction_complete( + // &self.testing_context, + // &payer_signer, + // current_state.auction_state(), + // &prepared_order_response, + // &prepared_custody_token, + // &self.testing_context.get_matching_engine_program_id(), + // config.expected_error.as_ref(), + // ) + // .await; + // match auction_state { + // AuctionState::Settled => TestingEngineState::AuctionSettled { + // base: current_state.base().clone(), + // initialized: current_state.initialized().unwrap().clone(), + // router_endpoints: current_state.router_endpoints().unwrap().clone(), + // auction_state: current_state.auction_state().clone(), + // fast_market_order: current_state.fast_market_order().cloned(), + // order_prepared: order_prepared_state.clone(), + // auction_accounts: current_state.auction_accounts().cloned(), + // }, + // _ => current_state.clone(), + // } + // } } diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index f054f8157..049f8a6c2 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -1,7 +1,7 @@ pub mod account_fixtures; pub mod airdrop; pub mod auction; -pub mod cctp_message; +// pub mod cctp_message; pub mod constants; pub mod mint; pub mod program_fixtures; From a9e9b643df0d29e32a5b1dad2d1b0fcaea0865c7 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 14:36:06 +0000 Subject: [PATCH 031/112] changed workflows solana yml --- .github/workflows/solana.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 8309254df..1f9b67c1d 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -5,7 +5,10 @@ on: push: branches: - main - pull_request: + pull_request: + branches: + - main + - 'shim/integration' paths: - 'solana/**' From 257bd5c5a75d93eec2cf219ca3721b999072b99d Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 14:53:20 +0000 Subject: [PATCH 032/112] Cargo toml edited and Makefile all targets removed --- solana/Cargo.toml | 4 ++-- solana/Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 0ea20e14f..e3b0e35cd 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -56,8 +56,8 @@ wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole wormhole-svm-shim = { path = "lib/wormhole/svm/wormhole-core-shims/crates/shim" } wormhole-svm-definitions = { path = "lib/wormhole/svm/wormhole-core-shims/crates/definitions" } -# [profile.release] -# overflow-checks = true +[profile.release] +overflow-checks = true # lto = "fat" # codegen-units = 1 diff --git a/solana/Makefile b/solana/Makefile index 1def5a753..60904f5d6 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -30,7 +30,7 @@ node_modules: .PHONY: cargo-test cargo-test: - cargo test --workspace --all-targets --features $(NETWORK) + cargo test --lib --workspace --features $(NETWORK) .PHONY: cargo-test-all cargo-test-all: From ad3a93399fa02e3d2101490dbd6f88b795cb3935 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 14:59:10 +0000 Subject: [PATCH 033/112] workflow recurses submodule --- .github/workflows/solana.yml | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 1f9b67c1d..ffa41cbd0 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -30,10 +30,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUSTC_VERSION }} + - name: Git Submodule Update + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git submodule update --init --recursive + working-directory: ./solana - name: make cargo-test-all run: make cargo-test-all working-directory: ./solana @@ -43,11 +51,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.RUSTC_VERSION }} components: clippy, rustfmt + - name: Git Submodule Update + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git submodule update --init --recursive + working-directory: ./solana - name: make lint run: make lint working-directory: ./solana @@ -57,14 +73,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - uses: metadaoproject/setup-anchor@v2 with: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - # - name: Set default Rust toolchain - # run: rustup default stable - # working-directory: ./solana + - name: Set default Rust toolchain + run: rustup default stable + working-directory: ./solana + - name: Git Submodule Update + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git submodule update --init --recursive + working-directory: ./solana - name: make check-idl run: make check-idl working-directory: ./solana @@ -74,6 +98,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -83,9 +109,15 @@ jobs: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - # - name: Set default Rust toolchain - # run: rustup default stable - # working-directory: ./solana + - name: Set default Rust toolchain + run: rustup default stable + working-directory: ./solana + - name: Git Submodule Update + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git submodule update --init --recursive + working-directory: ./solana - name: make anchor-test run: make anchor-test working-directory: ./solana From d27e8e284e8ee79edcb63b02a23a5c2f697a29e8 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 17:28:09 +0000 Subject: [PATCH 034/112] manual clippy lint fixes --- solana/Cargo.toml | 3 +- .../processor/close_fast_market_order.rs | 7 +- .../src/fallback/processor/helpers.rs | 7 +- .../processor/initialise_fast_market_order.rs | 7 +- .../fallback/processor/place_initial_offer.rs | 54 ++++++------ .../fallback/processor/process_instruction.rs | 12 +-- .../auction/offer/place_initial/cctp_shim.rs | 6 +- .../src/state/fast_market_order.rs | 84 ++++++++++--------- .../tests/integration_tests.rs | 6 +- .../tests/shimful/fast_market_order_shim.rs | 46 +++++----- .../tests/shimless/initialize.rs | 6 +- .../tests/shimless/make_offer.rs | 4 +- .../tests/testing_engine/engine.rs | 8 +- .../matching-engine/tests/utils/constants.rs | 14 ++++ .../matching-engine/tests/utils/router.rs | 75 +++++++---------- .../matching-engine/tests/utils/setup.rs | 44 +++++++--- .../matching-engine/tests/utils/vaa.rs | 2 +- 17 files changed, 205 insertions(+), 180 deletions(-) diff --git a/solana/Cargo.toml b/solana/Cargo.toml index e3b0e35cd..9f2878e98 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -68,6 +68,7 @@ overflow-checks = true [workspace.lints.clippy] correctness = { priority = -1, level = "warn"} +inconsistent_digit_grouping = "allow" ### See clippy.toml. unnecessary_lazy_evaluations = "allow" @@ -82,7 +83,7 @@ cast_possible_wrap = "deny" cast_precision_loss = "deny" cast_sign_loss = "deny" eq_op = "deny" -expect_used = "deny" +expect_used = "allow" # TODO: Change this back once we get there float_cmp = "deny" integer_division = "deny" large_futures = "deny" diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index e635a1ca0..220b80e42 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -65,8 +65,7 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { { return Err(ProgramError::InvalidAccountData.into()).map_err(|e: Error| { e.with_pubkeys(( - Pubkey::try_from(fast_market_order_data.close_account_refund_recipient) - .expect("Failed to convert close account refund recipient to pubkey"), + Pubkey::from(fast_market_order_data.close_account_refund_recipient), close_account_refund_recipient.key(), )) }); @@ -74,7 +73,9 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { // Transfer the lamports from the fast market order to the close account refund recipient let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut(); - **close_account_refund_recipient.lamports.borrow_mut() += **fast_market_order_lamports; + **close_account_refund_recipient.lamports.borrow_mut() = + (**close_account_refund_recipient.lamports.borrow()) + .saturating_add(**fast_market_order_lamports); **fast_market_order_lamports = 0; Ok(()) diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index f88f17e26..41633677f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -31,7 +31,7 @@ pub fn create_account_reliably( payer_key, account_key, lamports, - data_len as u64, + u64::try_from(data_len).unwrap(), // lol it won't do ::from program_id, ); @@ -105,8 +105,9 @@ pub fn create_account_reliably( let cpi_data = &mut cpi_ix.data; cpi_data[0] = 8; // allocate selector - cpi_data[4..12].copy_from_slice(&(data_len as u64).to_le_bytes()); - + cpi_data[4..12].copy_from_slice(&u64::try_from(data_len).unwrap().to_le_bytes()); + // ↑ + // It won't do ::from but it'll do ::try_from invoke_signed_unchecked(&cpi_ix, accounts, signer_seeds)?; } diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 4d1c19788..302fb98ab 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -159,7 +159,7 @@ pub fn initialise_fast_market_order( // Start of fast market order account creation // ------------------------------------------------------------------------------------------------ let fast_market_order_key = fast_market_order_account.key(); - let space = 8 + std::mem::size_of::(); + let space = 8_usize.saturating_add(std::mem::size_of::()); let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrderState::SEED_PREFIX, @@ -201,13 +201,14 @@ pub fn initialise_fast_market_order( let fast_market_order_bytes = bytemuck::bytes_of(&data.fast_market_order); // Ensure the destination has enough space - if fast_market_order_account_data.len() < 8 + fast_market_order_bytes.len() { + if fast_market_order_account_data.len() < 8_usize.saturating_add(fast_market_order_bytes.len()) + { msg!("Account data buffer too small"); return Err(MatchingEngineError::AccountDataTooSmall.into()); } // Write the fast_market_order struct to the account - fast_market_order_account_data[8..8 + fast_market_order_bytes.len()] + fast_market_order_account_data[8..8_usize.saturating_add(fast_market_order_bytes.len())] .copy_from_slice(fast_market_order_bytes); // End of fast market order account creation // ------------------------------------------------------------------------------------------------ diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index e61119292..83074a5c8 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -154,7 +154,7 @@ impl VaaMessageBodyHeader { if fast_market_order.redeemer_message_length > 0 { payload.extend_from_slice( &fast_market_order.redeemer_message - [..fast_market_order.redeemer_message_length as usize], + [..usize::from(fast_market_order.redeemer_message_length)], ); } message_body.extend_from_slice(&payload); @@ -329,7 +329,7 @@ pub fn place_initial_offer_cctp_shim( // Check contents of fast_market_order { - let deadline = fast_market_order_zero_copy.deadline as i64; + let deadline = i64::from(fast_market_order_zero_copy.deadline); let expiration = i64::from(vaa_time).saturating_add(crate::VAA_AUCTION_EXPIRATION_TIME); let current_time = Clock::get().unwrap().unix_timestamp; if !((deadline == 0 || current_time < deadline) && current_time < expiration) { @@ -362,7 +362,7 @@ pub fn place_initial_offer_cctp_shim( crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, auction_key.as_ref(), ], - &program_id, + program_id, ); if auction_custody_token_pda != auction_custody_token.key() { msg!( @@ -407,7 +407,7 @@ pub fn place_initial_offer_cctp_shim( let auction_space = 8 + Auction::INIT_SPACE; let (pda, bump) = Pubkey::find_program_address( &[Auction::SEED_PREFIX, vaa_message_digest.as_ref()], - &program_id, + program_id, ); if pda != auction_key { @@ -422,7 +422,7 @@ pub fn place_initial_offer_cctp_shim( auction_account.lamports(), auction_space, accounts, - &program_id, + program_id, auction_signer_seeds, )?; // Borrow the account data mutably @@ -472,7 +472,7 @@ pub fn place_initial_offer_cctp_shim( let auction_bytes = auction_to_write .try_to_vec() .map_err(|_| MatchingEngineError::BorshDeserializationError)?; - data[8..8 + auction_bytes.len()].copy_from_slice(&auction_bytes); + data[8..8_usize.saturating_add(auction_bytes.len())].copy_from_slice(&auction_bytes); // ------------------------------------------------------------------------------------------------ // End of initialisation of auction account @@ -509,30 +509,32 @@ pub fn place_initial_offer_cctp_shim( #[cfg(test)] mod tests { + use crate::state::FastMarketOrderParams; + use super::*; #[test] fn test_bytemuck() { - let test_fast_market_order = FastMarketOrderState::new( - 1000000000000000000, - 1000000000000000000, - 1000000000, - 1, - 0, - [0_u8; 32], - [0_u8; 32], - [0_u8; 32], - 0, - 0, - [0_u8; 512], - [0_u8; 32], - 0, - 0, - 0, - 0, - 0, - [0_u8; 32], - ); + let test_fast_market_order = FastMarketOrderState::new(FastMarketOrderParams { + amount_in: 1000000000000000000, + min_amount_out: 1000000000000000000, + deadline: 1000000000, + target_chain: 1, + redeemer_message_length: 0, + redeemer: [0_u8; 32], + sender: [0_u8; 32], + refund_address: [0_u8; 32], + max_fee: 0, + init_auction_fee: 0, + redeemer_message: [0_u8; 512], + close_account_refund_recipient: [0_u8; 32], + vaa_sequence: 0, + vaa_timestamp: 0, + vaa_nonce: 0, + vaa_emitter_chain: 0, + vaa_consistency_level: 0, + vaa_emitter_address: [0_u8; 32], + }); let bytes = bytemuck::bytes_of(&test_fast_market_order); assert!(bytes.len() == std::mem::size_of::()); } diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 6644334dc..9edc6ff96 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -43,13 +43,13 @@ pub fn process_instruction( let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data).unwrap(); match instruction { FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(data) => { - initialise_fast_market_order(accounts, &data) + initialise_fast_market_order(accounts, data) } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { close_fast_market_order(accounts) } FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { - place_initial_offer_cctp_shim(accounts, &data) + place_initial_offer_cctp_shim(accounts, data) } // FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { // handle_execute_order_shim(accounts) // } @@ -68,12 +68,12 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { match instruction_data[..8].try_into().unwrap() { FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { Some(Self::PlaceInitialOfferCctpShim( - &PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), )) } FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => Some( - Self::InitialiseFastMarketOrder(&bytemuck::from_bytes(&instruction_data[8..])), + Self::InitialiseFastMarketOrder(bytemuck::from_bytes(&instruction_data[8..])), ), FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) @@ -97,7 +97,7 @@ impl FallbackMatchingEngineInstruction<'_> { Self::PlaceInitialOfferCctpShim(data) => { // Calculate the total capacity needed let data_slice = bytemuck::bytes_of(*data); - let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length // Create a vector with the calculated capacity let mut out = Vec::with_capacity(total_capacity); @@ -125,7 +125,7 @@ impl FallbackMatchingEngineInstruction<'_> { // } Self::InitialiseFastMarketOrder(data) => { let data_slice = bytemuck::bytes_of(*data); - let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length let mut out = Vec::with_capacity(total_capacity); diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs index 34b83397e..986af0166 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs @@ -50,8 +50,8 @@ pub struct PlaceInitialOfferCctpShim<'info> { #[account( init, payer = payer, - space = 8 + std::mem::size_of::(), - // │ └─ FastMarketOrderState account data size + space = 8_usize.saturating_add(std::mem::size_of::()), + // │ └─ FastMarketOrderState account data size // └─ Anchor discriminator (8 bytes) seeds = [ FastMarketOrderState::SEED_PREFIX, @@ -209,7 +209,7 @@ impl VaaMessageBody { } fn to_vec(&self) -> Vec { - vec![ + [ self.vaa_time.to_be_bytes().as_ref(), self.nonce.to_be_bytes().as_ref(), self.emitter_chain.to_be_bytes().as_ref(), diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index b639e570d..d9e98332e 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -48,46 +48,48 @@ pub struct FastMarketOrder { _padding: [u8; 5], } +pub struct FastMarketOrderParams { + pub amount_in: u64, + pub min_amount_out: u64, + pub deadline: u32, + pub target_chain: u16, + pub redeemer_message_length: u16, + pub redeemer: [u8; 32], + pub sender: [u8; 32], + pub refund_address: [u8; 32], + pub max_fee: u64, + pub init_auction_fee: u64, + pub redeemer_message: [u8; 512], + pub close_account_refund_recipient: [u8; 32], + pub vaa_sequence: u64, + pub vaa_timestamp: u32, + pub vaa_nonce: u32, + pub vaa_emitter_chain: u16, + pub vaa_consistency_level: u8, + pub vaa_emitter_address: [u8; 32], +} + impl FastMarketOrder { - pub fn new( - amount_in: u64, - min_amount_out: u64, - deadline: u32, - target_chain: u16, - redeemer_message_length: u16, - redeemer: [u8; 32], - sender: [u8; 32], - refund_address: [u8; 32], - max_fee: u64, - init_auction_fee: u64, - redeemer_message: [u8; 512], - close_account_refund_recipient: [u8; 32], - vaa_sequence: u64, - vaa_timestamp: u32, - vaa_nonce: u32, - vaa_emitter_chain: u16, - vaa_consistency_level: u8, - vaa_emitter_address: [u8; 32], - ) -> Self { + pub fn new(params: FastMarketOrderParams) -> Self { Self { - amount_in, - min_amount_out, - deadline, - target_chain, - redeemer_message_length, - redeemer, - sender, - refund_address, - max_fee, - init_auction_fee, - redeemer_message, - close_account_refund_recipient, - vaa_sequence, - vaa_timestamp, - vaa_nonce, - vaa_emitter_chain, - vaa_consistency_level, - vaa_emitter_address, + amount_in: params.amount_in, + min_amount_out: params.min_amount_out, + deadline: params.deadline, + target_chain: params.target_chain, + redeemer_message_length: params.redeemer_message_length, + redeemer: params.redeemer, + sender: params.sender, + refund_address: params.refund_address, + max_fee: params.max_fee, + init_auction_fee: params.init_auction_fee, + redeemer_message: params.redeemer_message, + close_account_refund_recipient: params.close_account_refund_recipient, + vaa_sequence: params.vaa_sequence, + vaa_timestamp: params.vaa_timestamp, + vaa_nonce: params.vaa_nonce, + vaa_emitter_chain: params.vaa_emitter_chain, + vaa_consistency_level: params.vaa_consistency_level, + vaa_emitter_address: params.vaa_emitter_address, _padding: [0_u8; 5], } } @@ -117,8 +119,10 @@ impl FastMarketOrder { payload.extend_from_slice(&self.deadline.to_be_bytes()); payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); if self.redeemer_message_length > 0 { - payload - .extend_from_slice(&self.redeemer_message[..self.redeemer_message_length as usize]); + payload.extend_from_slice( + // uisize try from should never fail + &self.redeemer_message[..usize::from(self.redeemer_message_length)], + ); } payload } diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index ddb06f6a8..7d190a9b7 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -1,6 +1,5 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; -use matching_engine::error::MatchingEngineError; use matching_engine::ID as PROGRAM_ID; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -195,7 +194,7 @@ pub async fn test_approve_usdc() { let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; let vaa_data = first_test_ft.vaa_data; - let actors = testing_context.testing_actors; + let actors = &testing_context.testing_actors; let solver = actors.solvers[0].clone(); let offer_price: u64 = 1__000_000; let program_id = PROGRAM_ID; @@ -234,9 +233,6 @@ pub async fn test_approve_usdc() { println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); let solver_token_account_info = testing_context - .test_context - .borrow_mut() - .banks_client .get_account(solver_token_account_address) .await .expect("Failed to query banks client for solver token account info") diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs index b4c8f19aa..d9c0ef239 100644 --- a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs @@ -14,7 +14,7 @@ use matching_engine::fallback::initialise_fast_market_order::{ InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, }; -use matching_engine::state::FastMarketOrder as FastMarketOrderState; +use matching_engine::state::{FastMarketOrder as FastMarketOrderState, FastMarketOrderParams}; use solana_sdk::transaction::VersionedTransaction; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; use std::rc::Rc; @@ -113,7 +113,7 @@ fn initialise_fast_market_order_fallback_instruction( }; InitialiseFastMarketOrderFallback { - program_id: program_id, + program_id, accounts: create_fast_market_order_accounts, data: InitialiseFastMarketOrderFallbackData::new(fast_market_order, guardian_set_bump), } @@ -148,7 +148,7 @@ pub async fn close_fast_market_order_fallback( .await .expect("Failed to get new blockhash"); let close_fast_market_order_ix = CloseFastMarketOrderFallback { - program_id: program_id, + program_id, accounts: CloseFastMarketOrderFallbackAccounts { fast_market_order: fast_market_order_address, close_account_refund_recipient: &refund_recipient_keypair.pubkey(), @@ -206,26 +206,26 @@ pub fn create_fast_market_order_state_from_vaa_data( fixed_array }; - let fast_market_order = FastMarketOrderState::new( - order.amount_in, - order.min_amount_out, - order.deadline, - order.target_chain, - order.redeemer_message.len() as u16, - order.redeemer, - order.sender, - order.refund_address, - order.max_fee, - order.init_auction_fee, - redeemer_message_fixed_length, - close_account_refund_recipient.to_bytes(), - vaa_data.sequence, - vaa_data.vaa_time, - vaa_data.nonce, - vaa_data.emitter_chain, - vaa_data.consistency_level, - vaa_data.emitter_address, - ); + let fast_market_order = FastMarketOrderState::new(FastMarketOrderParams { + amount_in: order.amount_in, + min_amount_out: order.min_amount_out, + deadline: order.deadline, + target_chain: order.target_chain, + redeemer_message_length: order.redeemer_message.len() as u16, + redeemer: order.redeemer, + sender: order.sender, + refund_address: order.refund_address, + max_fee: order.max_fee, + init_auction_fee: order.init_auction_fee, + redeemer_message: redeemer_message_fixed_length, + close_account_refund_recipient: close_account_refund_recipient.to_bytes(), + vaa_sequence: vaa_data.sequence, + vaa_timestamp: vaa_data.vaa_time, + vaa_nonce: vaa_data.nonce, + vaa_emitter_chain: vaa_data.emitter_chain, + vaa_consistency_level: vaa_data.consistency_level, + vaa_emitter_address: vaa_data.emitter_address, + }); assert_eq!(fast_market_order.redeemer, order.redeemer); assert_eq!( diff --git a/solana/programs/matching-engine/tests/shimless/initialize.rs b/solana/programs/matching-engine/tests/shimless/initialize.rs index 9cea6ca10..3bb6b0098 100644 --- a/solana/programs/matching-engine/tests/shimless/initialize.rs +++ b/solana/programs/matching-engine/tests/shimless/initialize.rs @@ -177,7 +177,7 @@ pub async fn initialize_program( .fee_recipient .token_account_address() .unwrap(), - cctp_mint_recipient: cctp_mint_recipient, + cctp_mint_recipient, usdc: matching_engine::accounts::Usdc { mint: usdc_mint_address, }, @@ -192,7 +192,7 @@ pub async fn initialize_program( // Create the instruction let instruction = Instruction { - program_id: program_id, + program_id, accounts: accounts.to_account_metas(None), data: ix_data.data(), }; @@ -235,7 +235,7 @@ pub async fn initialize_program( let initialize_addresses = InitializeAddresses { custodian_address: custodian, auction_config_address: auction_config, - cctp_mint_recipient: cctp_mint_recipient, + cctp_mint_recipient, }; Some(InitializeFixture { custodian: custodian_data, diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs index 028a02cd3..d6ab8dd42 100644 --- a/solana/programs/matching-engine/tests/shimless/make_offer.rs +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -132,7 +132,7 @@ pub async fn place_initial_offer_shimless( } let initial_offer_ix_anchor = Instruction { - program_id: program_id, + program_id, accounts: account_metas, data: initial_offer_ix.data(), }; @@ -226,7 +226,7 @@ pub async fn improve_offer( // TODO: Figure out better name for this let improve_offer_ix_anchor = Instruction { - program_id: program_id, + program_id, accounts: account_metas, data: improve_offer_ix.data(), }; diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 9d011031f..035ba1de4 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -76,9 +76,7 @@ pub struct TestingEngine { impl TestingEngine { pub async fn new(testing_context: TestingContext) -> Self { - Self { - testing_context: testing_context, - } + Self { testing_context } } pub async fn execute(&self, instruction_chain: Vec) { @@ -280,8 +278,8 @@ impl TestingEngine { router_endpoints: current_state.router_endpoints().cloned(), fast_market_order: FastMarketOrderAccountCreatedState { fast_market_order_address: fast_market_order_account, - fast_market_order_bump: fast_market_order_bump, - fast_market_order: fast_market_order, + fast_market_order_bump, + fast_market_order, }, guardian_set_state: GuardianSetState { guardian_set_address: guardian_set_pubkey, diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 89ef48699..736cbeb7f 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -144,6 +144,20 @@ pub enum Chain { Polygon, } +impl Chain { + pub fn to_index(&self) -> usize { + match self { + Chain::Solana => 0, + Chain::Ethereum => 1, + Chain::Avalanche => 2, + Chain::Optimism => 3, + Chain::Arbitrum => 4, + Chain::Base => 5, + Chain::Polygon => 6, + } + } +} + // Registered Token Routers lazy_static::lazy_static! { pub static ref REGISTERED_TOKEN_ROUTERS: std::collections::HashMap> = { diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index e6176ce6e..5ec5e5a83 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -97,14 +97,14 @@ impl TestEndpointInfo { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: mint_recipient.to_bytes(), - protocol: protocol, + protocol, } } else { Self { chain: chain.to_chain_id(), address: address.to_bytes(), mint_recipient: address.to_bytes(), - protocol: protocol, + protocol, } } } @@ -176,7 +176,7 @@ pub fn get_router_endpoint_address(program_id: Pubkey, encoded_chain: &[u8; 2]) } pub async fn add_cctp_router_endpoint_ix( - test_context: &Rc>, + testing_context: &TestingContext, admin_owner_or_assistant: Pubkey, admin_custodian: Pubkey, admin_keypair: &Keypair, @@ -190,7 +190,7 @@ pub async fn add_cctp_router_endpoint_ix( mint: usdc_mint_address, }; - let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); + let encoded_chain = (chain.to_chain_id()).to_be_bytes(); let router_endpoint_address = get_router_endpoint_address(program_id, &encoded_chain); let local_custody_token_address = Pubkey::find_program_address( @@ -200,7 +200,7 @@ pub async fn add_cctp_router_endpoint_ix( .0; let accounts = AddCctpRouterEndpointAccounts { - payer: test_context.borrow().payer.pubkey(), + payer: testing_context.test_context.borrow().payer.pubkey(), admin, router_endpoint: router_endpoint_address, local_custody_token: local_custody_token_address, @@ -217,7 +217,7 @@ pub async fn add_cctp_router_endpoint_ix( let ix_data = AddCctpRouterEndpoint { args: AddCctpRouterEndpointArgs { chain: chain.to_chain_id(), - cctp_domain: CHAIN_TO_DOMAIN[chain as usize].1, + cctp_domain: CHAIN_TO_DOMAIN[chain.to_index()].1, address: registered_token_router_address, mint_recipient: None, }, @@ -225,36 +225,29 @@ pub async fn add_cctp_router_endpoint_ix( .data(); let instruction = Instruction { - program_id: program_id, + program_id, accounts: accounts.to_account_metas(None), data: ix_data, }; - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&testing_context.test_context.borrow().payer.pubkey()), + ); // TODO: Figure out who the signers are - let new_blockhash = test_context - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); + let new_blockhash = testing_context.get_new_latest_blockhash().await; transaction.sign( - &[&test_context.borrow().payer, &admin_keypair], + &[&testing_context.test_context.borrow().payer, &admin_keypair], new_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - test_context - .borrow_mut() - .banks_client + let versioned_transaction = VersionedTransaction::from(transaction); + testing_context .process_transaction(versioned_transaction) .await - .unwrap(); + .expect("Failed to process transaction"); - let endpoint_account = test_context - .borrow_mut() - .banks_client + let endpoint_account = testing_context .get_account(router_endpoint_address) .await .unwrap() @@ -269,7 +262,7 @@ pub async fn add_cctp_router_endpoint_ix( &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { - domain: CHAIN_TO_DOMAIN[chain as usize].1, + domain: CHAIN_TO_DOMAIN[chain.to_index()].1, }, ); test_router_endpoint @@ -294,11 +287,11 @@ pub async fn add_local_router_endpoint_ix( // Create the local token router let local_token_router = LocalTokenRouter { token_router_program, - token_router_emitter: token_router_emitter.clone(), + token_router_emitter, token_router_mint_recipient, }; let chain = Chain::Solana; - let encoded_chain = (chain.to_chain_id() as u16).to_be_bytes(); + let encoded_chain = (chain.to_chain_id()).to_be_bytes(); let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); @@ -314,39 +307,30 @@ pub async fn add_local_router_endpoint_ix( let ix_data = AddLocalRouterEndpoint {}.data(); let instruction = Instruction { - program_id: program_id, + program_id, accounts: accounts.to_account_metas(None), data: ix_data, }; let mut transaction = Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); - let new_blockhash = test_context - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); + let new_blockhash = testing_context.get_new_latest_blockhash().await; transaction.sign( &[&test_context.borrow().payer, &admin_keypair], new_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - test_context - .borrow_mut() - .banks_client + let versioned_transaction = VersionedTransaction::from(transaction); + testing_context .process_transaction(versioned_transaction) .await - .unwrap(); + .expect("Failed to process transaction"); - let endpoint_account = test_context - .borrow_mut() - .banks_client + let endpoint_account = testing_context .get_account(router_endpoint_address) .await - .unwrap() - .unwrap(); + .expect("Failed to get account") + .expect("Account not found"); let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); @@ -373,7 +357,6 @@ pub async fn create_cctp_router_endpoint( let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let program_id = testing_context.get_matching_engine_program_id(); - let test_context = &testing_context.test_context; let token_messenger = match chain { Chain::Arbitrum => fixture_accounts.arbitrum_remote_token_messenger, Chain::Ethereum => fixture_accounts.ethereum_remote_token_messenger, @@ -382,7 +365,7 @@ pub async fn create_cctp_router_endpoint( } }; let token_router_endpoint = add_cctp_router_endpoint_ix( - test_context, + testing_context, admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index c6bdf3b32..999e47e58 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -47,6 +47,8 @@ cfg_if::cfg_if! { } else if #[cfg(feature = "localnet")] { //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); // const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); + const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; } } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; @@ -205,6 +207,36 @@ impl TestingContext { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } + pub async fn get_new_latest_blockhash(&self) -> solana_program::hash::Hash { + let mut ctx = self.test_context.borrow_mut(); + let hash = ctx + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + drop(ctx); + hash + } + + pub async fn process_transaction( + &self, + transaction: impl Into, + ) -> Result<(), BanksClientError> { + let mut ctx = self.test_context.borrow_mut(); + ctx.banks_client.process_transaction(transaction).await?; + drop(ctx); + Ok(()) + } + + pub async fn get_account( + &self, + address: Pubkey, + ) -> Result, BanksClientError> { + let mut ctx = self.test_context.borrow_mut(); + let account = ctx.banks_client.get_account(address).await?; + drop(ctx); + Ok(account) + } + pub async fn execute_and_verify_logs( &self, transaction: impl Into, @@ -212,10 +244,7 @@ impl TestingContext { ) { tracing::init_tracing(); tracing::clear_logs(); - self.test_context - .borrow_mut() - .banks_client - .process_transaction(transaction) + self.process_transaction(transaction) .await .expect("Transaction should not fail"); let logs = tracing::get_logs(); @@ -233,12 +262,7 @@ impl TestingContext { transaction: impl Into, expected_error: Option<&ExpectedError>, ) { - let tx_result = self - .test_context - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await; + let tx_result = self.process_transaction(transaction).await; if let Some(expected_error) = expected_error { let tx_error = tx_result.expect_err(&format!( "Expected error {:?}, but transaction succeeded", diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index a800762bb..d3e54100b 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -130,7 +130,7 @@ impl PostedVaaData { let vaa_data_serialized = serialize_with_discriminator(self).unwrap(); let lamports = solana_sdk::rent::Rent::default().minimum_balance(vaa_data_serialized.len()); let vaa_account = Account { - lamports: lamports, + lamports, data: vaa_data_serialized, owner: CORE_BRIDGE_PID, executable: false, From 1730e2d3ae1dc408b6ed9befa778a654109986da Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 18:41:23 +0000 Subject: [PATCH 035/112] clippy auto edits --- solana/Cargo.lock | 5 +- solana/Cargo.toml | 2 +- solana/clippy.toml | 5 +- solana/programs/matching-engine/Cargo.toml | 1 + .../tests/integration_tests.rs | 14 +-- .../tests/shimful/fast_market_order_shim.rs | 6 +- .../tests/shimful/post_message.rs | 15 +-- .../tests/shimful/shims_make_offer.rs | 4 +- .../tests/shimful/verify_shim.rs | 46 ++++------ .../tests/shimless/initialize.rs | 39 ++++---- .../tests/shimless/make_offer.rs | 13 ++- .../tests/testing_engine/config.rs | 77 +++------------- .../tests/testing_engine/engine.rs | 33 +++---- .../tests/testing_engine/state.rs | 3 +- .../tests/utils/account_fixtures.rs | 18 ++-- .../matching-engine/tests/utils/airdrop.rs | 11 +-- .../matching-engine/tests/utils/auction.rs | 33 ++++--- .../matching-engine/tests/utils/constants.rs | 4 +- .../matching-engine/tests/utils/mod.rs | 2 +- .../tests/utils/program_fixtures.rs | 2 +- .../matching-engine/tests/utils/router.rs | 66 +++++--------- .../matching-engine/tests/utils/setup.rs | 91 ++++++++++++------- .../tests/utils/token_account.rs | 2 +- .../matching-engine/tests/utils/vaa.rs | 85 +++++++---------- 24 files changed, 251 insertions(+), 326 deletions(-) diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 1e6b7ec59..e5a70aaf3 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "aquamarine" @@ -2545,6 +2545,7 @@ version = "0.0.0" dependencies = [ "anchor-lang", "anchor-spl", + "anyhow", "base64 0.22.1", "bincode", "bs58 0.5.0", diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 9f2878e98..45a2ed597 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -83,7 +83,7 @@ cast_possible_wrap = "deny" cast_precision_loss = "deny" cast_sign_loss = "deny" eq_op = "deny" -expect_used = "allow" # TODO: Change this back once we get there +expect_used = "deny" float_cmp = "deny" integer_division = "deny" large_futures = "deny" diff --git a/solana/clippy.toml b/solana/clippy.toml index b06464d4e..1db49eeef 100644 --- a/solana/clippy.toml +++ b/solana/clippy.toml @@ -4,4 +4,7 @@ disallowed-methods = [ { path = "std::option::Option::map_or", reason = "prefer `map_or_else` for lazy evaluation" }, { path = "std::option::Option::ok_or", reason = "prefer `ok_or_else` for lazy evaluation" }, { path = "std::option::Option::unwrap_or", reason = "prefer `unwrap_or_else` for lazy evaluation" }, -] \ No newline at end of file +] + +[expect_used] +ignore-tests = true \ No newline at end of file diff --git a/solana/programs/matching-engine/Cargo.toml b/solana/programs/matching-engine/Cargo.toml index 923bc3465..d8ad99609 100644 --- a/solana/programs/matching-engine/Cargo.toml +++ b/solana/programs/matching-engine/Cargo.toml @@ -61,6 +61,7 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-log = "0.2.0" once_cell = "1.8" +anyhow = "1.0.97" wormhole-svm-shim.workspace = true wormhole-svm-definitions.workspace = true diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index 7d190a9b7..81ce81896 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -119,15 +119,10 @@ pub async fn test_post_message_shims() { None, ) .await; - let actors = testing_context.testing_actors; + let actors = &testing_context.testing_actors; let emitter_signer = actors.owner.keypair(); let payer_signer = actors.solvers[0].keypair(); - set_up_post_message_transaction_test( - &testing_context.test_context, - &payer_signer, - &emitter_signer, - ) - .await; + set_up_post_message_transaction_test(&testing_context, &payer_signer, &emitter_signer).await; } #[tokio::test] @@ -222,13 +217,14 @@ pub async fn test_approve_usdc() { // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = shimful::verify_shim::create_guardian_signatures( - &testing_context.test_context, + &testing_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, None, ) - .await; + .await + .expect("Failed to create guardian signatures"); println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs index d9c0ef239..95c3f4a45 100644 --- a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs @@ -141,9 +141,7 @@ pub async fn close_fast_market_order_fallback( expected_error: Option<&ExpectedError>, ) { let program_id = &testing_context.get_matching_engine_program_id(); - let test_ctx = &testing_context.test_context; - let recent_blockhash = test_ctx - .borrow_mut() + let recent_blockhash = testing_context .get_new_latest_blockhash() .await .expect("Failed to get new blockhash"); @@ -211,7 +209,7 @@ pub fn create_fast_market_order_state_from_vaa_data( min_amount_out: order.min_amount_out, deadline: order.deadline, target_chain: order.target_chain, - redeemer_message_length: order.redeemer_message.len() as u16, + redeemer_message_length: u16::try_from(order.redeemer_message.len()).unwrap(), redeemer: order.redeemer, sender: order.sender, refund_address: order.refund_address, diff --git a/solana/programs/matching-engine/tests/shimful/post_message.rs b/solana/programs/matching-engine/tests/shimful/post_message.rs index 15363a721..4960d6971 100644 --- a/solana/programs/matching-engine/tests/shimful/post_message.rs +++ b/solana/programs/matching-engine/tests/shimful/post_message.rs @@ -1,4 +1,4 @@ -use crate::utils::constants::*; +use crate::utils::{constants::*, setup::TestingContext}; use solana_program_test::ProgramTestContext; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, @@ -36,7 +36,7 @@ impl BumpCosts { } fn bump_cu_cost(bump: u8) -> u64 { - 1_500 * (255 - u64::from(bump)) + 1_500 * (255_u64.saturating_sub(u64::from(bump))) } } @@ -50,12 +50,11 @@ impl BumpCosts { /// * `payer_signer` - The payer signer keypair /// * `emitter_signer` - The emitter signer keypair pub async fn set_up_post_message_transaction_test( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, emitter_signer: &Rc, ) { - let recent_blockhash = test_ctx - .borrow_mut() + let recent_blockhash = testing_context .get_new_latest_blockhash() .await .expect("Could not get last blockhash"); @@ -66,12 +65,14 @@ pub async fn set_up_post_message_transaction_test( recent_blockhash, ); let details = { - let out = test_ctx - .borrow_mut() + let test_ctx = &testing_context.test_context; + let mut ctx = test_ctx.borrow_mut(); + let out = ctx .banks_client .simulate_transaction(transaction) .await .unwrap(); + drop(ctx); assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); out.simulation_details.unwrap() }; diff --git a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs index ec38512e8..685332c89 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs @@ -36,6 +36,7 @@ use std::rc::Rc; /// /// * The expected error is reached /// * If successful, the solver's USDC balance should decrease by the offer price +#[allow(clippy::too_many_arguments)] pub async fn place_initial_offer_fallback( testing_context: &TestingContext, payer_signer: &Rc, @@ -143,7 +144,8 @@ pub async fn place_initial_offer_fallback( offer_price, }, }; - let new_auction_state = utils::auction::AuctionState::Active(new_active_auction_state); + let new_auction_state = + utils::auction::AuctionState::Active(Box::new(new_active_auction_state)); Some(InitialOfferPlacedState { auction_state: new_auction_state, }) diff --git a/solana/programs/matching-engine/tests/shimful/verify_shim.rs b/solana/programs/matching-engine/tests/shimful/verify_shim.rs index ff49376fa..1b509c9c6 100644 --- a/solana/programs/matching-engine/tests/shimful/verify_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/verify_shim.rs @@ -1,7 +1,8 @@ -use crate::utils; use crate::utils::constants::*; +use crate::utils::{self, setup::TestingContext}; use anchor_lang::prelude::*; -use solana_program_test::ProgramTestContext; +use anyhow::Result as AnyhowResult; + use solana_sdk::{ compute_budget::ComputeBudgetInstruction, hash::Hash, @@ -9,7 +10,7 @@ use solana_sdk::{ signature::{Keypair, Signer}, transaction::VersionedTransaction, }; -use std::cell::RefCell; + use std::rc::Rc; use std::str::FromStr; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; @@ -31,36 +32,34 @@ use wormhole_svm_shim::verify_vaa; /// /// * `(guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump)` - The guardian set pubkey, the guardian signatures pubkey and the guardian set bump pub async fn create_guardian_signatures( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, vaa_data: &utils::vaa::PostedVaaData, wormhole_program_id: &Pubkey, guardian_signature_signer: Option<&Rc>, -) -> (Pubkey, Pubkey, u8) { +) -> AnyhowResult<(Pubkey, Pubkey, u8)> { let new_keypair = Rc::new(Keypair::new()); - let guardian_signature_signer = guardian_signature_signer.unwrap_or(&new_keypair); + let guardian_signature_signer = guardian_signature_signer.unwrap_or_else(|| &new_keypair); let (guardian_set_pubkey, guardian_set_bump) = wormhole_svm_definitions::find_guardian_set_address( 0_u32.to_be_bytes(), - &wormhole_program_id, + wormhole_program_id, ); - let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY) - .expect("Failed to parse guardian secret key"); + let guardian_secret_key = secp256k1::SecretKey::from_str(GUARDIAN_SECRET_KEY)?; let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); let guardian_signatures_pubkey = add_guardian_signatures_account( - test_ctx, + testing_context, payer_signer, guardian_signature_signer, vec![guardian_set_signatures], 0, ) - .await - .expect("Failed to post guardian signatures"); - ( + .await?; + Ok(( guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump, - ) + )) } /// Add a guardian signatures account @@ -79,31 +78,22 @@ pub async fn create_guardian_signatures( /// /// * `guardian_signatures_pubkey` - The guardian signatures pubkey async fn add_guardian_signatures_account( - test_ctx: &Rc>, + testing_context: &TestingContext, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32, -) -> Result { - let new_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); +) -> AnyhowResult { + let new_blockhash = testing_context.get_new_latest_blockhash().await?; let transaction = post_signatures_transaction( payer_signer, signatures_signer, guardian_set_index, - guardian_signatures.len() as u8, + u8::try_from(guardian_signatures.len())?, &guardian_signatures, new_blockhash, ); - test_ctx - .borrow_mut() - .banks_client - .process_transaction(transaction) - .await - .expect("Failed to add guardian signatures account"); + testing_context.process_transaction(transaction).await?; Ok(signatures_signer.pubkey()) } diff --git a/solana/programs/matching-engine/tests/shimless/initialize.rs b/solana/programs/matching-engine/tests/shimless/initialize.rs index 3bb6b0098..5aad91d78 100644 --- a/solana/programs/matching-engine/tests/shimless/initialize.rs +++ b/solana/programs/matching-engine/tests/shimless/initialize.rs @@ -32,11 +32,11 @@ pub struct InitializeFixture { impl InitializeFixture { pub fn get_custodian_address(&self) -> Pubkey { - self.addresses.custodian_address.clone() + self.addresses.custodian_address } pub fn get_auction_config_address(&self) -> Pubkey { - self.addresses.auction_config_address.clone() + self.addresses.auction_config_address } } @@ -121,17 +121,17 @@ impl Default for AuctionParametersConfig { } } -impl Into for AuctionParametersConfig { - fn into(self) -> AuctionParameters { +impl From for AuctionParameters { + fn from(val: AuctionParametersConfig) -> Self { AuctionParameters { - user_penalty_reward_bps: self.user_penalty_reward_bps, - initial_penalty_bps: self.initial_penalty_bps, - duration: self.duration, - grace_period: self.grace_period, - penalty_period: self.penalty_period, - min_offer_delta_bps: self.min_offer_delta_bps, - security_deposit_base: self.security_deposit_base, - security_deposit_bps: self.security_deposit_bps, + user_penalty_reward_bps: val.user_penalty_reward_bps, + initial_penalty_bps: val.initial_penalty_bps, + duration: val.duration, + grace_period: val.grace_period, + penalty_period: val.penalty_period, + min_offer_delta_bps: val.min_offer_delta_bps, + security_deposit_base: val.security_deposit_base, + security_deposit_bps: val.security_deposit_bps, } } } @@ -199,11 +199,10 @@ pub async fn initialize_program( // Create and sign transaction let mut transaction = Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); - let new_blockhash = test_context - .borrow_mut() + let new_blockhash = testing_context .get_new_latest_blockhash() .await - .expect("Failed to get new blockhash"); + .expect("Could not get new blockhash"); transaction.sign( &[ &test_context.borrow().payer, @@ -213,19 +212,15 @@ pub async fn initialize_program( ); // Process transaction - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); - + let versioned_transaction = VersionedTransaction::from(transaction); testing_context .execute_and_verify_transaction(versioned_transaction, expected_error) .await; if expected_error.is_none() { // Verify the results - let custodian_account = test_context - .borrow_mut() - .banks_client - .get_account(custodian.clone()) + let custodian_account = testing_context + .get_account(custodian) .await .expect("Failed to get custodian account") .expect("Custodian account not found"); diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/programs/matching-engine/tests/shimless/make_offer.rs index d6ab8dd42..56463ccf4 100644 --- a/solana/programs/matching-engine/tests/shimless/make_offer.rs +++ b/solana/programs/matching-engine/tests/shimless/make_offer.rs @@ -76,9 +76,7 @@ pub async fn place_initial_offer_shimless( { // Check if solver has already approved usdc let usdc_account = accounts.solver.token_account_address().unwrap(); - let usdc_account_info = test_ctx - .borrow_mut() - .banks_client + let usdc_account_info = testing_context .get_account(usdc_account) .await .unwrap() @@ -150,7 +148,7 @@ pub async fn place_initial_offer_shimless( // If the transaction failed and we expected it to pass, we would not get here if expected_error.is_none() { - AuctionState::Active(ActiveAuctionState { + AuctionState::Active(Box::new(ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: accounts.auction_config, @@ -164,12 +162,13 @@ pub async fn place_initial_offer_shimless( offer_token: accounts.offer_token, offer_price: initial_offer_ix.offer_price, }, - }) + })) } else { AuctionState::Inactive } } +#[allow(clippy::too_many_arguments)] pub async fn improve_offer( testing_context: &TestingContext, program_id: Pubkey, @@ -248,7 +247,7 @@ pub async fn improve_offer( .get_active_auction() .unwrap() .initial_offer; - Some(AuctionState::Active(ActiveAuctionState { + Some(AuctionState::Active(Box::new(ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: auction_config, @@ -258,7 +257,7 @@ pub async fn improve_offer( offer_token, offer_price, }, - })) + }))) } else { None } diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs index 639bed65f..634ec19fc 100644 --- a/solana/programs/matching-engine/tests/testing_engine/config.rs +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -13,21 +13,17 @@ pub struct ExpectedError { pub error_string: String, } -#[derive(Clone)] +pub struct ExpectedLog { + pub log_message: String, + pub count: usize, +} + +#[derive(Clone, Default)] pub struct InitializeInstructionConfig { pub auction_parameters_config: AuctionParametersConfig, pub expected_error: Option, } -impl Default for InitializeInstructionConfig { - fn default() -> Self { - Self { - auction_parameters_config: AuctionParametersConfig::default(), - expected_error: None, - } - } -} - pub struct CreateCctpRouterEndpointsInstructionConfig { pub chains: HashSet, pub expected_error: Option, @@ -41,7 +37,7 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { } } } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct InitializeFastMarketOrderShimInstructionConfig { pub fast_market_order_id: u32, pub close_account_refund_recipient: Option, // If none defaults to solver 0 pubkey, @@ -49,18 +45,7 @@ pub struct InitializeFastMarketOrderShimInstructionConfig { pub expected_error: Option, } -impl Default for InitializeFastMarketOrderShimInstructionConfig { - fn default() -> Self { - Self { - fast_market_order_id: 0, - close_account_refund_recipient: None, - payer_signer: None, - expected_error: None, - } - } -} - -#[derive(Clone)] +#[derive(Clone, Default)] pub struct PrepareOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, pub solver_index: usize, @@ -69,18 +54,7 @@ pub struct PrepareOrderInstructionConfig { pub expected_log_message: Option, } -impl Default for PrepareOrderInstructionConfig { - fn default() -> Self { - Self { - fast_market_order_address: None, - solver_index: 0, - payer_signer: None, - expected_error: None, - expected_log_message: None, - } - } -} -#[derive(Clone)] +#[derive(Clone, Default)] pub struct ExecuteOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, pub solver_index: usize, @@ -88,48 +62,19 @@ pub struct ExecuteOrderInstructionConfig { pub expected_error: Option, } -impl Default for ExecuteOrderInstructionConfig { - fn default() -> Self { - Self { - fast_market_order_address: None, - solver_index: 0, - payer_signer: None, - expected_error: None, - } - } -} - -#[derive(Clone)] +#[derive(Clone, Default)] pub struct SettleAuctionInstructionConfig { pub payer_signer: Option>, pub expected_error: Option, } -impl Default for SettleAuctionInstructionConfig { - fn default() -> Self { - Self { - payer_signer: None, - expected_error: None, - } - } -} -#[derive(Clone)] +#[derive(Clone, Default)] pub struct CloseFastMarketOrderShimInstructionConfig { pub close_account_refund_recipient_keypair: Option>, // If none, will use the solver 0 keypair pub fast_market_order_address: OverwriteCurrentState, // If none, will use the fast market order address from the current state pub expected_error: Option, } -impl Default for CloseFastMarketOrderShimInstructionConfig { - fn default() -> Self { - Self { - close_account_refund_recipient_keypair: None, - fast_market_order_address: None, - expected_error: None, - } - } -} - pub struct PlaceInitialOfferInstructionConfig { pub solver_index: usize, pub offer_price: u64, diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 035ba1de4..51b1a54cb 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -235,21 +235,22 @@ impl TestingEngine { &fast_transfer_vaa.vaa_data, config .close_account_refund_recipient - .unwrap_or(self.testing_context.testing_actors.solvers[0].pubkey()), + .unwrap_or_else(|| self.testing_context.testing_actors.solvers[0].pubkey()), ); let payer_signer = config .payer_signer .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = create_guardian_signatures( - &self.testing_context.test_context, + &self.testing_context, &payer_signer, &fast_transfer_vaa.vaa_data, &self.testing_context.get_wormhole_program_id(), None, ) - .await; + .await + .expect("Failed to create guardian signatures"); let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( &[ @@ -297,16 +298,16 @@ impl TestingEngine { config: &CloseFastMarketOrderShimInstructionConfig, ) -> TestingEngineState { // Get the fast market order account from the current state. If it is not present, panic - let fast_market_order_account = config.fast_market_order_address.unwrap_or( + let fast_market_order_account = config.fast_market_order_address.unwrap_or_else(|| { current_state .fast_market_order() .expect("Fast market order account not found") - .fast_market_order_address, - ); + .fast_market_order_address + }); let close_account_refund_recipient = config .close_account_refund_recipient_keypair .clone() - .unwrap_or(self.testing_context.testing_actors.solvers[0].keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.solvers[0].keypair()); shimful::fast_market_order_shim::close_fast_market_order_fallback( &self.testing_context, @@ -338,7 +339,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let solver = self .testing_context .testing_actors @@ -349,7 +350,7 @@ impl TestingEngine { let fast_vaa = ¤t_state .base() .vaas - .get(0) + .first() .expect("Failed to get vaa pair") .fast_transfer_vaa; let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); @@ -420,7 +421,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, self.testing_context.get_matching_engine_program_id(), @@ -451,12 +452,12 @@ impl TestingEngine { current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, ) -> TestingEngineState { - let fast_market_order_address = config.fast_market_order_address.unwrap_or( + let fast_market_order_address = config.fast_market_order_address.unwrap_or_else(|| { current_state .fast_market_order() .expect("Fast market order is not created") - .fast_market_order_address, - ); + .fast_market_order_address + }); let router_endpoints = current_state .router_endpoints() .expect("Router endpoints are not created"); @@ -464,7 +465,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let auction_config_address = current_state .auction_config_address() .expect("Auction config address not found"); @@ -488,7 +489,7 @@ impl TestingEngine { shimful::shims_make_offer::place_initial_offer_fallback( &self.testing_context, &payer_signer, - &fast_vaa_data, + fast_vaa_data, solver, &fast_market_order_address, &auction_accounts, diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/programs/matching-engine/tests/testing_engine/state.rs index 8ab6d5fa2..bb530ffb0 100644 --- a/solana/programs/matching-engine/tests/testing_engine/state.rs +++ b/solana/programs/matching-engine/tests/testing_engine/state.rs @@ -65,6 +65,7 @@ pub struct GuardianSetState { } // The main state enum that reflects all possible instruction states +#[allow(dead_code)] #[derive(Clone)] pub enum TestingEngineState { Uninitialized(BaseState), @@ -278,7 +279,7 @@ impl TestingEngineState { } pub fn get_first_test_vaa_pair(&self) -> &TestVaaPair { - self.base().vaas.get(0).unwrap() + self.base().vaas.first().unwrap() } // Convenience methods for common fields diff --git a/solana/programs/matching-engine/tests/utils/account_fixtures.rs b/solana/programs/matching-engine/tests/utils/account_fixtures.rs index 9f7aa6d2d..8ca8e7c05 100644 --- a/solana/programs/matching-engine/tests/utils/account_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/account_fixtures.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::{pubkey, Pubkey}; +use anyhow::Result as AnyhowResult; use serde_json::Value; use solana_program_test::ProgramTest; use std::{fs, str::FromStr}; @@ -64,7 +65,7 @@ impl FixtureAccounts { /// * `program_test` - The program test instance pub fn add_lookup_table_hack(program_test: &mut ProgramTest) { let filename = "tests/fixtures/lup.json"; - let account_fixture = read_account_from_file(filename); + let account_fixture = read_account_from_file(filename).unwrap(); program_test.add_account_with_file_data( account_fixture.address, account_fixture.lamports, @@ -84,7 +85,7 @@ impl FixtureAccounts { /// * `filename` - The path to the JSON fixture file fn add_account_from_file(program_test: &mut ProgramTest, filename: &str) -> AccountFixture { // Parse the JSON file to an AccountFixture struct - let account_fixture = read_account_from_file(filename); + let account_fixture = read_account_from_file(filename).unwrap(); // Add the account to the program test program_test.add_account_with_base64_data( account_fixture.address, @@ -102,8 +103,6 @@ struct AccountFixture { pub base_64_data: String, } -// FIXME: This code is not being used, remove it - /// Reads an account from a JSON fixture file /// /// Reads the JSON file and parses it into a Value object that is used to extract the lamports, address, and owner values. @@ -115,13 +114,12 @@ struct AccountFixture { /// # Returns /// /// An AccountFixture struct containing the address, owner, lamports, and filename. -fn read_account_from_file(filename: &str) -> AccountFixture { +fn read_account_from_file(filename: &str) -> AnyhowResult { // Read the JSON file - let data = fs::read_to_string(filename).expect(&format!("Unable to read file {}", filename)); + let data = fs::read_to_string(filename)?; // Parse the JSON - let json: Value = - serde_json::from_str(&data).expect(&format!("Unable to parse JSON {}", filename)); + let json: Value = serde_json::from_str(&data)?; // Extract the lamports value let lamports = json["account"]["lamports"] @@ -146,10 +144,10 @@ fn read_account_from_file(filename: &str) -> AccountFixture { let base_64_data = json["account"]["data"][0] .as_str() .expect("data field not found or invalid"); - AccountFixture { + Ok(AccountFixture { address, owner, lamports, base_64_data: base_64_data.to_string(), - } + }) } diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/programs/matching-engine/tests/utils/airdrop.rs index 79d42516f..32146ec13 100644 --- a/solana/programs/matching-engine/tests/utils/airdrop.rs +++ b/solana/programs/matching-engine/tests/utils/airdrop.rs @@ -34,6 +34,7 @@ pub async fn airdrop( ); ctx.banks_client.process_transaction(tx).await.unwrap(); + drop(ctx); } pub async fn airdrop_usdc( @@ -63,13 +64,11 @@ pub async fn airdrop_usdc( new_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(tx) - .expect("Failed to convert transaction to versioned transaction"); - - test_context - .borrow_mut() - .banks_client + let versioned_transaction = VersionedTransaction::from(tx); + let mut ctx = test_context.borrow_mut(); + ctx.banks_client .process_transaction(versioned_transaction) .await .unwrap(); + drop(ctx); } diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/programs/matching-engine/tests/utils/auction.rs index af1f2c42d..36d698e99 100644 --- a/solana/programs/matching-engine/tests/utils/auction.rs +++ b/solana/programs/matching-engine/tests/utils/auction.rs @@ -3,8 +3,8 @@ use anchor_lang::prelude::*; use super::router::TestRouterEndpoints; use super::setup::{Solver, TestingContext, TransferDirection}; use super::Chain; +use anyhow::{anyhow, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; - #[derive(Clone)] pub struct AuctionAccounts { pub posted_fast_vaa: Option, @@ -17,9 +17,10 @@ pub struct AuctionAccounts { pub usdc_mint: Pubkey, } +#[allow(dead_code)] #[derive(Clone)] pub enum AuctionState { - Active(ActiveAuctionState), + Active(Box), Settled, Inactive, } @@ -68,7 +69,13 @@ impl AuctionAccounts { router_endpoints.get_endpoint_address(Chain::Arbitrum), router_endpoints.get_endpoint_address(Chain::Ethereum), ), - _ => panic!("Unsupported transfer direction"), + TransferDirection::Other => { + println!("Unsupported transfer direction, defaulting to FromEthereumToArbitrum"); + ( + router_endpoints.get_endpoint_address(Chain::Ethereum), + router_endpoints.get_endpoint_address(Chain::Arbitrum), + ) + } }; Self { posted_fast_vaa, @@ -84,29 +91,26 @@ impl AuctionAccounts { } impl ActiveAuctionState { - pub async fn verify_auction(&self, testing_context: &TestingContext) { - let test_ctx = &testing_context.test_context; - let auction_account = test_ctx - .borrow_mut() - .banks_client + pub async fn verify_auction(&self, testing_context: &TestingContext) -> AnyhowResult<()> { + let auction_account = testing_context .get_account(self.auction_address) - .await - .unwrap() + .await? .expect("Failed to get auction account"); let mut data_ref = auction_account.data.as_ref(); - let auction_account_data: Auction = - AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + let auction_account_data: Auction = AccountDeserialize::try_deserialize(&mut data_ref)?; let auction_info = auction_account_data.info.unwrap(); let expected_auction_info = AuctionInfo { - config_id: 0, // TODO: Figure this out + config_id: 0, custody_token_bump: 254, // TODO: Figure this out vaa_sequence: 0, // No need to cehck against this source_chain: { match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => 3, TransferDirection::FromArbitrumToEthereum => 23, - _ => panic!("Unsupported transfer direction"), + TransferDirection::Other => { + return Err(anyhow!("Unsupported transfer direction")); + } } }, best_offer_token: self.best_offer.offer_token, @@ -127,5 +131,6 @@ impl ActiveAuctionState { auction_info.redeemer_message_len, expected_auction_info.redeemer_message_len ); + Ok(()) } } diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 736cbeb7f..2b4391889 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -145,7 +145,7 @@ pub enum Chain { } impl Chain { - pub fn to_index(&self) -> usize { + pub fn as_index(&self) -> usize { match self { Chain::Solana => 0, Chain::Ethereum => 1, @@ -174,7 +174,7 @@ lazy_static::lazy_static! { // Chain ID mapping impl Chain { - pub fn to_chain_id(&self) -> u16 { + pub fn as_chain_id(&self) -> u16 { match self { Chain::Solana => 1, Chain::Ethereum => 2, diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index 049f8a6c2..1e5f5f986 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -8,6 +8,6 @@ pub mod program_fixtures; pub mod router; pub mod setup; pub mod token_account; -pub mod tracing; +// pub mod tracing; pub mod vaa; pub use constants::*; diff --git a/solana/programs/matching-engine/tests/utils/program_fixtures.rs b/solana/programs/matching-engine/tests/utils/program_fixtures.rs index 6cd66be71..550de2ad2 100644 --- a/solana/programs/matching-engine/tests/utils/program_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/program_fixtures.rs @@ -29,7 +29,7 @@ pub fn initialise_upgrade_manager( // Add the program data to the program test // Compute lamports from length of program data - let program_data_data = get_program_data(owner_pubkey.clone()); + let program_data_data = get_program_data(owner_pubkey); let lamports = solana_sdk::rent::Rent::default().minimum_balance(program_data_data.len()); let account = solana_sdk::account::Account { diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 5ec5e5a83..4ea55f1fc 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -4,7 +4,7 @@ use super::constants::*; use super::setup::TestingContext; use super::token_account::create_token_account_for_pda; use anchor_lang::prelude::*; -use anchor_lang::Discriminator; + use anchor_lang::{InstructionData, ToAccountMetas}; use common::wormhole_cctp_solana::cctp::token_messenger_minter_program::RemoteTokenMessenger; use matching_engine::accounts::{ @@ -37,34 +37,6 @@ fn generate_admin(owner_or_assistant: Pubkey, custodian: Pubkey) -> Admin { } } -#[allow(dead_code)] -async fn print_account_discriminator( - test_context: &Rc>, - address: &Pubkey, -) { - println!("Printing account discriminator for address: {:?}", address); - let account = test_context - .borrow_mut() - .banks_client - .get_account(*address) - .await - .unwrap() - .expect("Account not found"); - - println!("Account data: {:?}", account.data); - - let account_owner = account.owner; - println!("Account owner: {:?}", account_owner); - - // Get first 8 bytes (discriminator) - let discriminator = &account.data[..8]; - println!("Account discriminator: {:?}", discriminator); - - // Compare with expected discriminator (WARNING: ASSUMPTION) - let expected = RemoteTokenMessenger::discriminator(); - println!("Expected discriminator: {:?}", expected); -} - /// A struct representing an endpoint info for testing purposes #[derive(Debug, Clone, Eq, PartialEq)] pub struct TestEndpointInfo { @@ -94,14 +66,14 @@ impl TestEndpointInfo { ) -> Self { if let Some(mint_recipient) = mint_recipient { Self { - chain: chain.to_chain_id(), + chain: chain.as_chain_id(), address: address.to_bytes(), mint_recipient: mint_recipient.to_bytes(), protocol, } } else { Self { - chain: chain.to_chain_id(), + chain: chain.as_chain_id(), address: address.to_bytes(), mint_recipient: address.to_bytes(), protocol, @@ -190,7 +162,7 @@ pub async fn add_cctp_router_endpoint_ix( mint: usdc_mint_address, }; - let encoded_chain = (chain.to_chain_id()).to_be_bytes(); + let encoded_chain = (chain.as_chain_id()).to_be_bytes(); let router_endpoint_address = get_router_endpoint_address(program_id, &encoded_chain); let local_custody_token_address = Pubkey::find_program_address( @@ -216,8 +188,8 @@ pub async fn add_cctp_router_endpoint_ix( .expect("Failed to convert registered token router address to bytes [u8; 32]"); let ix_data = AddCctpRouterEndpoint { args: AddCctpRouterEndpointArgs { - chain: chain.to_chain_id(), - cctp_domain: CHAIN_TO_DOMAIN[chain.to_index()].1, + chain: chain.as_chain_id(), + cctp_domain: CHAIN_TO_DOMAIN[chain.as_index()].1, address: registered_token_router_address, mint_recipient: None, }, @@ -235,7 +207,10 @@ pub async fn add_cctp_router_endpoint_ix( Some(&testing_context.test_context.borrow().payer.pubkey()), ); // TODO: Figure out who the signers are - let new_blockhash = testing_context.get_new_latest_blockhash().await; + let new_blockhash = testing_context + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); transaction.sign( &[&testing_context.test_context.borrow().payer, &admin_keypair], new_blockhash, @@ -253,8 +228,8 @@ pub async fn add_cctp_router_endpoint_ix( .unwrap() .unwrap(); - let endpoint_data = - RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()).unwrap(); + let endpoint_data = RouterEndpoint::try_deserialize(&mut endpoint_account.data.as_slice()) + .expect("Failed to deserialize endpoint data"); let test_router_endpoint = TestRouterEndpoint::from((&endpoint_data, router_endpoint_address)); test_router_endpoint.verify_endpoint_info( @@ -262,7 +237,7 @@ pub async fn add_cctp_router_endpoint_ix( &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { - domain: CHAIN_TO_DOMAIN[chain.to_index()].1, + domain: CHAIN_TO_DOMAIN[chain.as_index()].1, }, ); test_router_endpoint @@ -291,7 +266,7 @@ pub async fn add_local_router_endpoint_ix( token_router_mint_recipient, }; let chain = Chain::Solana; - let encoded_chain = (chain.to_chain_id()).to_be_bytes(); + let encoded_chain = (chain.as_chain_id()).to_be_bytes(); let (router_endpoint_address, _bump) = Pubkey::find_program_address(&[RouterEndpoint::SEED_PREFIX, &encoded_chain], &program_id); @@ -314,7 +289,10 @@ pub async fn add_local_router_endpoint_ix( let mut transaction = Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); - let new_blockhash = testing_context.get_new_latest_blockhash().await; + let new_blockhash = testing_context + .get_new_latest_blockhash() + .await + .expect("Could not get new blockhash"); transaction.sign( &[&test_context.borrow().payer, &admin_keypair], new_blockhash, @@ -364,7 +342,8 @@ pub async fn create_cctp_router_endpoint( panic!("Unsupported chain"); } }; - let token_router_endpoint = add_cctp_router_endpoint_ix( + + add_cctp_router_endpoint_ix( testing_context, admin_owner_or_assistant, custodian_address, @@ -374,8 +353,7 @@ pub async fn create_cctp_router_endpoint( usdc_mint_address, chain, ) - .await; - token_router_endpoint + .await } pub async fn create_all_router_endpoints_test( @@ -417,6 +395,8 @@ pub async fn create_all_router_endpoints_test( TestRouterEndpoints(endpoints) } +// TODO: Remove this allow once all instructions are implemented +#[allow(dead_code)] pub async fn get_remote_token_messenger( test_context: &Rc>, address: Pubkey, diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 999e47e58..9a4b3f0e2 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -7,19 +7,19 @@ use super::program_fixtures::{ initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, initialise_verify_shims, initialise_wormhole_core_bridge, }; -use super::tracing; use super::vaa::{create_vaas_test_with_chain_and_address, TestVaaPair, TestVaaPairs, VaaArgs}; use super::{ airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, }; use super::{Chain, REGISTERED_TOKEN_ROUTERS}; -use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::{ExpectedError, ExpectedLog}; use anchor_lang::AccountDeserialize; use anchor_spl::token::{ spl_token::{self, instruction::approve}, TokenAccount, }; +use anyhow::Result as AnyhowResult; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::{BanksClientError, ProgramTest, ProgramTestContext}; use solana_sdk::instruction::InstructionError; @@ -173,10 +173,7 @@ impl TestingContext { } pub async fn verify_vaas(&self) { - self.testing_state - .vaas - .verify_posted_vaas(&self.test_context) - .await; + self.testing_state.vaas.verify_posted_vaas(self).await; } pub fn get_vaa_pair(&self, index: usize) -> Option { @@ -207,14 +204,11 @@ impl TestingContext { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } - pub async fn get_new_latest_blockhash(&self) -> solana_program::hash::Hash { + pub async fn get_new_latest_blockhash(&self) -> AnyhowResult { let mut ctx = self.test_context.borrow_mut(); - let hash = ctx - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); + let hash = ctx.get_new_latest_blockhash().await?; drop(ctx); - hash + Ok(hash) } pub async fn process_transaction( @@ -237,23 +231,53 @@ impl TestingContext { Ok(account) } - pub async fn execute_and_verify_logs( + /// Simulates a transaction and verifies that the logs contain the expected lines + /// + /// # Arguments + /// + /// * `transaction` - The transaction to simulate + /// * `expected_logs` - A vector of strings that should be present in the logs + /// + /// # Returns + /// + /// The simulation details if the transaction was successful and all expected logs were found + #[allow(dead_code)] + pub async fn simulate_and_verify_logs( &self, transaction: impl Into, - expected_log: &String, - ) { - tracing::init_tracing(); - tracing::clear_logs(); - self.process_transaction(transaction) - .await - .expect("Transaction should not fail"); - let logs = tracing::get_logs(); + expected_logs: Vec, + ) -> AnyhowResult<()> { + let mut ctx = self.test_context.borrow_mut(); + let simulation_result = ctx.banks_client.simulate_transaction(transaction).await?; + drop(ctx); + + // Verify the transaction succeeded assert!( - logs.contains(expected_log), - "Expected log {:?} not found in {:?}", - expected_log, - logs + simulation_result.result.clone().unwrap().is_ok(), + "Transaction simulation failed: {:?}", + simulation_result.result ); + + let details = simulation_result + .simulation_details + .expect("No simulation details available"); + + // Verify all expected logs are present + for expected_log in expected_logs { + let expected_log_count = expected_log.count; + let expected_log_message = &expected_log.log_message; + let found = details + .logs + .iter() + .filter(|log| log.contains(expected_log_message)) + .count(); + assert!( + found == expected_log_count, + "Expected log {} not found in program logs", + expected_log.log_message + ); + } + Ok(()) } // TODO: Edit to handle multiple instructions in a single transaction @@ -286,7 +310,8 @@ impl TestingContext { ); } _ => { - panic!( + assert!( + false, "Expected program error {:?}, but got: {:?}", expected_error.error_string, tx_error ); @@ -333,11 +358,12 @@ impl Solver { amount: u64, ) { // If signer pubkeys are empty, it means that the owner is the signer - let last_blockhash = test_context - .borrow_mut() + let mut ctx = test_context.borrow_mut(); + let last_blockhash = ctx .get_new_latest_blockhash() .await .expect("Failed to get new blockhash"); + drop(ctx); let approve_ix = approve( &spl_token::ID, &self.token_account_address().unwrap(), @@ -353,13 +379,12 @@ impl Solver { &[&self.actor.keypair()], last_blockhash, ); - - test_context - .borrow_mut() - .banks_client + let mut ctx = test_context.borrow_mut(); + ctx.banks_client .process_transaction(transaction) .await .expect("Failed to approve USDC"); + drop(ctx); } pub async fn get_balance(&self, test_context: &Rc>) -> u64 { @@ -510,6 +535,8 @@ impl TestingActors { } } +// TODO: Remove this allow once all instructions are implemented +#[allow(dead_code)] pub async fn fast_forward_slots(test_context: &Rc>, num_slots: u64) { // Get the current slot let mut current_slot = test_context diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/programs/matching-engine/tests/utils/token_account.rs index 3ea41d4fc..9f9b1f25f 100644 --- a/solana/programs/matching-engine/tests/utils/token_account.rs +++ b/solana/programs/matching-engine/tests/utils/token_account.rs @@ -88,7 +88,7 @@ pub async fn create_token_account_for_pda( let mut ctx = test_context.borrow_mut(); // Get the ATA address - let ata = anchor_spl::associated_token::get_associated_token_address(&pda, mint); + let ata = anchor_spl::associated_token::get_associated_token_address(pda, mint); // Create the create_ata instruction let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index d3e54100b..2ebccbb36 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -6,17 +6,18 @@ use secp256k1::SecretKey as SecpSecretKey; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use super::constants::Chain; +use super::setup::TestingContext; use super::CHAIN_TO_DOMAIN; use super::constants::CORE_BRIDGE_PID; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use solana_program::keccak; -use solana_program_test::{ProgramTest, ProgramTestContext}; +use solana_program_test::{ProgramTest}; use solana_sdk::account::Account; -use std::cell::RefCell; + use std::ops::{Deref, DerefMut}; -use std::rc::Rc; + pub trait DataDiscriminator { const DISCRIMINATOR: &'static [u8]; @@ -73,7 +74,7 @@ impl PostedVaaData { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs() as u32; - let emitter_chain = chain.to_chain_id(); + let emitter_chain = chain.as_chain_id(); Self { consistency_level: 1, vaa_time: timestamp, @@ -107,7 +108,7 @@ impl PostedVaaData { // Sign the message hash with the guardian key let secp = secp256k1::SECP256K1; let msg = secp256k1::Message::from_digest(self.digest()); - let recoverable_signature = secp.sign_ecdsa_recoverable(&msg, &guardian_secret_key); + let recoverable_signature = secp.sign_ecdsa_recoverable(&msg, guardian_secret_key); let mut signature_bytes = [0u8; GUARDIAN_SIGNATURE_LENGTH]; // First byte is the index signature_bytes[0] = index; @@ -200,7 +201,7 @@ pub struct TestVaa { impl TestVaa { pub fn get_vaa_pubkey(&self) -> Pubkey { - self.vaa_pubkey.clone() + self.vaa_pubkey } pub fn get_vaa_data(&self) -> &PostedVaaData { @@ -214,20 +215,12 @@ pub enum TestVaaKind { FastTransfer, } +#[derive(Default)] pub struct CreateDepositAndFastTransferParams { pub deposit_params: CreateDepositParams, pub fast_transfer_params: CreateFastTransferParams, } -impl Default for CreateDepositAndFastTransferParams { - fn default() -> Self { - Self { - deposit_params: CreateDepositParams::default(), - fast_transfer_params: CreateFastTransferParams::default(), - } - } -} - impl CreateDepositAndFastTransferParams { pub fn verify(&self) { assert!( @@ -333,7 +326,7 @@ impl TestVaaPair { source_address, refund_address, destination_address, - cctp_nonce: cctp_nonce as u32, + cctp_nonce: u32::try_from(cctp_nonce).unwrap(), sequence, deposit_vaa: TestVaa { kind: TestVaaKind::Deposit, @@ -355,20 +348,18 @@ impl TestVaaPair { pub fn add_to_test(&self, program_test: &mut ProgramTest) { self.deposit_vaa .vaa_data - .create_vaa_account(program_test, self.deposit_vaa.vaa_pubkey.clone()); + .create_vaa_account(program_test, self.deposit_vaa.vaa_pubkey); self.fast_transfer_vaa .vaa_data - .create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey.clone()); + .create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey); } - pub async fn verify_posted_vaa_pair(&self, test_context: &Rc>) { + pub async fn verify_posted_vaa_pair(&self, testing_context: &TestingContext) { let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); { - let deposit_vaa = test_context - .borrow_mut() - .banks_client - .get_account(self.deposit_vaa.vaa_pubkey.clone()) + let deposit_vaa = testing_context + .get_account(self.deposit_vaa.vaa_pubkey) .await .unwrap(); assert!(deposit_vaa.is_some(), "Deposit VAA not found"); @@ -379,10 +370,8 @@ impl TestVaaPair { } { - let fast_transfer_vaa = test_context - .borrow_mut() - .banks_client - .get_account(self.fast_transfer_vaa.vaa_pubkey.clone()) + let fast_transfer_vaa = testing_context + .get_account(self.fast_transfer_vaa.vaa_pubkey) .await .unwrap(); assert!(fast_transfer_vaa.is_some(), "Fast transfer VAA not found"); @@ -398,6 +387,7 @@ impl TestVaaPair { } } +#[allow(clippy::too_many_arguments)] pub fn create_deposit_message( token_mint: Pubkey, source_address: ChainAddress, @@ -414,8 +404,8 @@ pub fn create_deposit_message( let deposit = Deposit { token_address: token_mint.to_bytes(), amount: ruint::aliases::U256::from(amount), - source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain as usize].1, - destination_cctp_domain: CHAIN_TO_DOMAIN[Chain::Solana as usize].1, // Hardcode solana as destination domain + source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain.as_index()].1, + destination_cctp_domain: CHAIN_TO_DOMAIN[Chain::Solana.as_index()].1, // Hardcode solana as destination domain cctp_nonce, burn_source: source_address.address.to_bytes(), // Token router address mint_recipient: cctp_mint_recipient.to_bytes(), // Mint recipient program id @@ -430,7 +420,7 @@ pub fn create_deposit_message( deposit.to_vec(), source_address.address, sequence, - vaa_nonce as u32, + vaa_nonce, ); let vaa_hash = posted_vaa_data.message_hash(); let vaa_hash_as_slice = vaa_hash.as_ref(); @@ -439,6 +429,7 @@ pub fn create_deposit_message( (vaa_address, posted_vaa_data, deposit) } +#[allow(clippy::too_many_arguments)] pub fn create_fast_transfer_message( start_timestamp: Option, source_address: ChainAddress, @@ -452,12 +443,14 @@ pub fn create_fast_transfer_message( init_auction_fee: u64, ) -> (Pubkey, PostedVaaData, FastMarketOrder) { // If start timestamp is not provided, set the deadline to 0 - let deadline = start_timestamp.map(|timestamp| timestamp + 10).unwrap_or(0); + let deadline = start_timestamp + .map(|timestamp| timestamp + 10) + .unwrap_or_default(); // Implements TypePrefixedPayload let fast_market_order = FastMarketOrder { amount_in, min_amount_out, - target_chain: destination_address.chain.to_chain_id(), + target_chain: destination_address.chain.as_chain_id(), redeemer: destination_address.address.to_bytes(), sender: source_address.address.to_bytes(), refund_address: refund_address.address.to_bytes(), // Not used so can be all zeros @@ -515,9 +508,11 @@ impl TestVaaPairs { cctp_mint_recipient: Pubkey, vaa_args: &VaaArgs, ) { - let sequence = vaa_args.sequence.unwrap_or(self.len() as u64); - let cctp_nonce = vaa_args.cctp_nonce.unwrap_or(sequence + 1); - let vaa_nonce = vaa_args.vaa_nonce.unwrap_or(0); + let sequence = vaa_args + .sequence + .unwrap_or_else(|| u64::try_from(self.len()).unwrap()); + let cctp_nonce = vaa_args.cctp_nonce.unwrap_or_else(|| sequence + 1); + let vaa_nonce = vaa_args.vaa_nonce.unwrap_or_default(); let is_posted = vaa_args.post_vaa; let create_deposit_and_fast_transfer_params = &vaa_args.create_deposit_and_fast_transfer_params; @@ -553,7 +548,7 @@ impl TestVaaPairs { ChainAddress::new_with_address(destination_chain, destination_address); let refund_address = source_address.clone(); self.add_ft( - mint_address.clone(), + mint_address, source_address, refund_address, destination_address, @@ -567,15 +562,16 @@ impl TestVaaPairs { } } - pub async fn verify_posted_vaas(&self, test_context: &Rc>) { + pub async fn verify_posted_vaas(&self, testing_context: &TestingContext) { for vaa_pair in self.0.iter() { if vaa_pair.is_posted() { - vaa_pair.verify_posted_vaa_pair(test_context).await; + vaa_pair.verify_posted_vaa_pair(testing_context).await; } } } } +#[derive(Default)] pub struct VaaArgs { pub sequence: Option, pub cctp_nonce: Option, @@ -585,19 +581,6 @@ pub struct VaaArgs { pub create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, } -impl Default for VaaArgs { - fn default() -> Self { - Self { - sequence: None, - cctp_nonce: None, - vaa_nonce: None, - start_timestamp: None, - post_vaa: false, - create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams::default(), - } - } -} - pub fn create_vaas_test_with_chain_and_address( program_test: &mut ProgramTest, mint_address: Pubkey, From 1bf2fb0cde8d70999bef6e169de8cff3329b58f6 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 19:31:07 +0000 Subject: [PATCH 036/112] linting done --- solana/clippy.toml | 3 +- .../tests/integration_tests.rs | 5 ++ .../tests/shimful/fast_market_order_shim.rs | 3 +- .../matching-engine/tests/shimful/mod.rs | 1 + .../tests/shimful/post_message.rs | 6 +- .../matching-engine/tests/shimless/mod.rs | 2 + .../tests/testing_engine/engine.rs | 6 +- .../tests/testing_engine/mod.rs | 2 + .../tests/utils/account_fixtures.rs | 2 +- .../matching-engine/tests/utils/constants.rs | 6 -- .../matching-engine/tests/utils/mod.rs | 5 ++ .../matching-engine/tests/utils/router.rs | 16 ++--- .../matching-engine/tests/utils/setup.rs | 55 ++++++++------ .../matching-engine/tests/utils/vaa.rs | 71 +++++++++++-------- 14 files changed, 103 insertions(+), 80 deletions(-) diff --git a/solana/clippy.toml b/solana/clippy.toml index 1db49eeef..b36810917 100644 --- a/solana/clippy.toml +++ b/solana/clippy.toml @@ -6,5 +6,4 @@ disallowed-methods = [ { path = "std::option::Option::unwrap_or", reason = "prefer `unwrap_or_else` for lazy evaluation" }, ] -[expect_used] -ignore-tests = true \ No newline at end of file +allow-expect-in-tests = true \ No newline at end of file diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index 81ce81896..f65492a6c 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -1,3 +1,8 @@ +#![allow(clippy::expect_used)] +#![allow(dead_code)] +#![allow(clippy::panic)] +#![allow(clippy::await_holding_refcell_ref)] + use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; use matching_engine::ID as PROGRAM_ID; diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs index 95c3f4a45..3eaa2754d 100644 --- a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs +++ b/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs @@ -62,8 +62,7 @@ pub async fn initialise_fast_market_order_fallback( &[payer_signer], recent_blockhash, ); - let versioned_transaction = VersionedTransaction::try_from(transaction) - .expect("Failed to convert transaction to versioned transaction"); + let versioned_transaction = VersionedTransaction::from(transaction); testing_context .execute_and_verify_transaction(versioned_transaction, expected_error) .await; diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs index d3b7aa41e..68a5fd8fa 100644 --- a/solana/programs/matching-engine/tests/shimful/mod.rs +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -1,3 +1,4 @@ +#![allow(clippy::expect_used)] pub mod fast_market_order_shim; pub mod post_message; // pub mod shims_execute_order; diff --git a/solana/programs/matching-engine/tests/shimful/post_message.rs b/solana/programs/matching-engine/tests/shimful/post_message.rs index 4960d6971..61093a1bd 100644 --- a/solana/programs/matching-engine/tests/shimful/post_message.rs +++ b/solana/programs/matching-engine/tests/shimful/post_message.rs @@ -1,5 +1,5 @@ use crate::utils::{constants::*, setup::TestingContext}; -use solana_program_test::ProgramTestContext; + use solana_sdk::{ compute_budget::ComputeBudgetInstruction, hash::Hash, @@ -7,7 +7,7 @@ use solana_sdk::{ signature::{Keypair, Signer}, transaction::VersionedTransaction, }; -use std::cell::RefCell; + use std::rc::Rc; use wormhole_svm_definitions::{ find_emitter_sequence_address, find_shim_message_address, solana::Finality, @@ -36,7 +36,7 @@ impl BumpCosts { } fn bump_cu_cost(bump: u8) -> u64 { - 1_500 * (255_u64.saturating_sub(u64::from(bump))) + 1_500_u64.saturating_mul(255_u64.saturating_sub(u64::from(bump))) } } diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs index f594cdb9a..a949c85a9 100644 --- a/solana/programs/matching-engine/tests/shimless/mod.rs +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::expect_used)] + // pub mod execute_order; pub mod initialize; pub mod make_offer; diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 51b1a54cb..7658a4d23 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -389,7 +389,8 @@ impl TestingEngine { .get_active_auction() .unwrap() .verify_auction(&self.testing_context) - .await; + .await + .expect("Could not verify auction state"); return TestingEngineState::InitialOfferPlaced { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), @@ -505,7 +506,8 @@ impl TestingEngine { .unwrap(); active_auction_state .verify_auction(&self.testing_context) - .await; + .await + .expect("Could not verify auction"); return TestingEngineState::InitialOfferPlaced { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), diff --git a/solana/programs/matching-engine/tests/testing_engine/mod.rs b/solana/programs/matching-engine/tests/testing_engine/mod.rs index abfec3e7d..09052261d 100644 --- a/solana/programs/matching-engine/tests/testing_engine/mod.rs +++ b/solana/programs/matching-engine/tests/testing_engine/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::expect_used)] + pub mod config; pub mod engine; pub mod state; diff --git a/solana/programs/matching-engine/tests/utils/account_fixtures.rs b/solana/programs/matching-engine/tests/utils/account_fixtures.rs index 8ca8e7c05..bc353d837 100644 --- a/solana/programs/matching-engine/tests/utils/account_fixtures.rs +++ b/solana/programs/matching-engine/tests/utils/account_fixtures.rs @@ -124,7 +124,7 @@ fn read_account_from_file(filename: &str) -> AnyhowResult { // Extract the lamports value let lamports = json["account"]["lamports"] .as_u64() - .expect(&format!("lamports field not found or invalid {}", filename)); + .unwrap_or_else(|| panic!("lamports field not found or invalid {}", filename)); // Extract the address value let address: Pubkey = solana_sdk::pubkey::Pubkey::from_str( diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index 2b4391889..f25f5c057 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -113,12 +113,6 @@ pub fn get_player_one_keypair() -> Keypair { Keypair::from_base58_string(PLAYER_ONE_KEYPAIR_B58) } -// TODO: Remove these constants if not ever used -// Other constants -#[allow(dead_code)] -pub const GOVERNANCE_EMITTER_ADDRESS: Pubkey = - solana_program::pubkey!("11111111111111111111111111111115"); - pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // Chain to cctp domain mapping diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/programs/matching-engine/tests/utils/mod.rs index 1e5f5f986..37fbd7628 100644 --- a/solana/programs/matching-engine/tests/utils/mod.rs +++ b/solana/programs/matching-engine/tests/utils/mod.rs @@ -1,3 +1,8 @@ +#![allow(clippy::expect_used)] +#![allow(dead_code)] +#![allow(clippy::panic)] +#![allow(clippy::await_holding_refcell_ref)] + pub mod account_fixtures; pub mod airdrop; pub mod auction; diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 4ea55f1fc..8567a7cbd 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -18,12 +18,12 @@ use matching_engine::state::EndpointInfo; use matching_engine::state::RouterEndpoint; use matching_engine::AddCctpRouterEndpointArgs; use matching_engine::LOCAL_CUSTODY_TOKEN_SEED_PREFIX; -use solana_program_test::ProgramTestContext; + use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; use solana_sdk::transaction::VersionedTransaction; -use std::cell::RefCell; + use std::collections::HashMap; use std::collections::HashSet; use std::ops::Deref; @@ -154,9 +154,9 @@ pub async fn add_cctp_router_endpoint_ix( admin_keypair: &Keypair, program_id: Pubkey, remote_token_messenger: Pubkey, - usdc_mint_address: Pubkey, chain: Chain, ) -> TestRouterEndpoint { + let usdc_mint_address = testing_context.get_usdc_mint_address(); let admin = generate_admin(admin_owner_or_assistant, admin_custodian); let usdc = matching_engine::accounts::Usdc { mint: usdc_mint_address, @@ -333,7 +333,6 @@ pub async fn create_cctp_router_endpoint( chain: Chain, ) -> TestRouterEndpoint { let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); - let usdc_mint_address = testing_context.get_usdc_mint_address(); let program_id = testing_context.get_matching_engine_program_id(); let token_messenger = match chain { Chain::Arbitrum => fixture_accounts.arbitrum_remote_token_messenger, @@ -350,7 +349,6 @@ pub async fn create_cctp_router_endpoint( admin_keypair.as_ref(), program_id, token_messenger, - usdc_mint_address, chain, ) .await @@ -395,15 +393,11 @@ pub async fn create_all_router_endpoints_test( TestRouterEndpoints(endpoints) } -// TODO: Remove this allow once all instructions are implemented -#[allow(dead_code)] pub async fn get_remote_token_messenger( - test_context: &Rc>, + testing_context: &TestingContext, address: Pubkey, ) -> RemoteTokenMessenger { - let remote_token_messenger_data = test_context - .borrow_mut() - .banks_client + let remote_token_messenger_data = testing_context .get_account(address) .await .unwrap() diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/programs/matching-engine/tests/utils/setup.rs index 9a4b3f0e2..70f11d01b 100644 --- a/solana/programs/matching-engine/tests/utils/setup.rs +++ b/solana/programs/matching-engine/tests/utils/setup.rs @@ -7,7 +7,9 @@ use super::program_fixtures::{ initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, initialise_verify_shims, initialise_wormhole_core_bridge, }; -use super::vaa::{create_vaas_test_with_chain_and_address, TestVaaPair, TestVaaPairs, VaaArgs}; +use super::vaa::{ + create_vaas_test_with_chain_and_address, ChainAndAddress, TestVaaPair, TestVaaPairs, VaaArgs, +}; use super::{ airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, @@ -206,8 +208,8 @@ impl TestingContext { pub async fn get_new_latest_blockhash(&self) -> AnyhowResult { let mut ctx = self.test_context.borrow_mut(); - let hash = ctx.get_new_latest_blockhash().await?; - drop(ctx); + let handle = ctx.get_new_latest_blockhash(); + let hash = handle.await?; Ok(hash) } @@ -216,9 +218,8 @@ impl TestingContext { transaction: impl Into, ) -> Result<(), BanksClientError> { let mut ctx = self.test_context.borrow_mut(); - ctx.banks_client.process_transaction(transaction).await?; - drop(ctx); - Ok(()) + let handle = ctx.banks_client.process_transaction(transaction); + handle.await } pub async fn get_account( @@ -310,8 +311,7 @@ impl TestingContext { ); } _ => { - assert!( - false, + panic!( "Expected program error {:?}, but got: {:?}", expected_error.error_string, tx_error ); @@ -535,25 +535,26 @@ impl TestingActors { } } -// TODO: Remove this allow once all instructions are implemented -#[allow(dead_code)] -pub async fn fast_forward_slots(test_context: &Rc>, num_slots: u64) { +pub async fn fast_forward_slots(testing_context: &TestingContext, num_slots: u64) { // Get the current slot - let mut current_slot = test_context + let mut current_slot = testing_context + .test_context .borrow_mut() .banks_client .get_root_slot() .await .unwrap(); - let target_slot = current_slot + num_slots; + let test_context = &testing_context.test_context; + + let target_slot = current_slot.saturating_add(num_slots); while current_slot < target_slot { // Warp to the next slot - note we need to borrow_mut() here test_context .borrow_mut() - .warp_to_slot(current_slot + 1) + .warp_to_slot(current_slot.saturating_add(1)) .expect("Failed to warp to slot"); - current_slot += 1; + current_slot = current_slot.saturating_add(1); } // Optionally, process a transaction to ensure the new slot is recognized @@ -655,10 +656,14 @@ pub async fn setup_environment( &mut pre_testing_context.program_test, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT, - Chain::Arbitrum, - Chain::Ethereum, - arbitrum_emitter_address, - ethereum_emitter_address, + ChainAndAddress { + chain: Chain::Arbitrum, + address: arbitrum_emitter_address, + }, + ChainAndAddress { + chain: Chain::Ethereum, + address: ethereum_emitter_address, + }, vaa_args, )) } @@ -667,10 +672,14 @@ pub async fn setup_environment( &mut pre_testing_context.program_test, USDC_MINT_ADDRESS, CCTP_MINT_RECIPIENT, - Chain::Ethereum, - Chain::Arbitrum, - ethereum_emitter_address, - arbitrum_emitter_address, + ChainAndAddress { + chain: Chain::Ethereum, + address: ethereum_emitter_address, + }, + ChainAndAddress { + chain: Chain::Arbitrum, + address: arbitrum_emitter_address, + }, vaa_args, )) } diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 2ebccbb36..62bfe3052 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -13,12 +13,11 @@ use super::constants::CORE_BRIDGE_PID; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use solana_program::keccak; -use solana_program_test::{ProgramTest}; +use solana_program_test::ProgramTest; use solana_sdk::account::Account; use std::ops::{Deref, DerefMut}; - pub trait DataDiscriminator { const DISCRIMINATOR: &'static [u8]; } @@ -70,10 +69,13 @@ impl PostedVaaData { sequence: u64, nonce: u32, ) -> Self { - let timestamp = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() as u32; + let timestamp = u32::try_from( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + ) + .unwrap(); let emitter_chain = chain.as_chain_id(); Self { consistency_level: 1, @@ -116,7 +118,7 @@ impl PostedVaaData { let (recovery_id, compact_sig) = recoverable_signature.serialize_compact(); // Recovery ID goes in byte 65 signature_bytes[1..65].copy_from_slice(&compact_sig); - signature_bytes[65] = i32::from(recovery_id) as u8; + signature_bytes[65] = u8::try_from(i32::from(recovery_id)).unwrap(); signature_bytes } @@ -149,8 +151,8 @@ pub fn deserialize_with_discriminator( if discriminant != T::DISCRIMINATOR { return None; } - let mut data = data[4..].to_vec(); - let message = T::try_from_slice(&mut data); + let data = data[4..].to_vec(); + let message = T::try_from_slice(&data); match message { Ok(message) => Some(message), Err(_) => None, @@ -181,7 +183,6 @@ impl PayloadDeserialized { } } - #[allow(dead_code)] pub fn get_fast_transfer(&self) -> Option { match self { Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), @@ -225,7 +226,10 @@ impl CreateDepositAndFastTransferParams { pub fn verify(&self) { assert!( self.fast_transfer_params.max_fee - > self.deposit_params.base_fee + self.fast_transfer_params.init_auction_fee, + > self + .deposit_params + .base_fee + .saturating_add(self.fast_transfer_params.init_auction_fee), "Max fee must be greater than the sum of the base fee and the init auction fee" ); assert!( @@ -280,6 +284,7 @@ pub struct TestVaaPair { } impl TestVaaPair { + #[allow(clippy::too_many_arguments)] pub fn new( start_timestamp: Option, token_mint: Pubkey, @@ -315,7 +320,7 @@ impl TestVaaPair { refund_address.clone(), destination_address.clone(), vaa_nonce, - sequence + 1, + sequence.saturating_add(1), create_fast_transfer_params.amount_in, create_fast_transfer_params.min_amount_out, create_fast_transfer_params.max_fee, @@ -444,7 +449,7 @@ pub fn create_fast_transfer_message( ) -> (Pubkey, PostedVaaData, FastMarketOrder) { // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp - .map(|timestamp| timestamp + 10) + .map(|timestamp| timestamp.saturating_add(10)) .unwrap_or_default(); // Implements TypePrefixedPayload let fast_market_order = FastMarketOrder { @@ -511,7 +516,9 @@ impl TestVaaPairs { let sequence = vaa_args .sequence .unwrap_or_else(|| u64::try_from(self.len()).unwrap()); - let cctp_nonce = vaa_args.cctp_nonce.unwrap_or_else(|| sequence + 1); + let cctp_nonce = vaa_args + .cctp_nonce + .unwrap_or_else(|| sequence.saturating_add(1)); let vaa_nonce = vaa_args.vaa_nonce.unwrap_or_default(); let is_posted = vaa_args.post_vaa; let create_deposit_and_fast_transfer_params = @@ -537,15 +544,18 @@ impl TestVaaPairs { program_test: &mut ProgramTest, mint_address: Pubkey, cctp_mint_recipient: Pubkey, - source_chain: Chain, - destination_chain: Chain, - source_address: [u8; 32], - destination_address: [u8; 32], + source_chain_and_address: ChainAndAddress, + destination_chain_and_address: ChainAndAddress, vaa_args: &VaaArgs, ) { - let source_address = ChainAddress::new_with_address(source_chain, source_address); - let destination_address = - ChainAddress::new_with_address(destination_chain, destination_address); + let source_address = ChainAddress::new_with_address( + source_chain_and_address.chain, + source_chain_and_address.address, + ); + let destination_address = ChainAddress::new_with_address( + destination_chain_and_address.chain, + destination_chain_and_address.address, + ); let refund_address = source_address.clone(); self.add_ft( mint_address, @@ -581,14 +591,17 @@ pub struct VaaArgs { pub create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, } +pub struct ChainAndAddress { + pub chain: Chain, + pub address: [u8; 32], +} + pub fn create_vaas_test_with_chain_and_address( program_test: &mut ProgramTest, mint_address: Pubkey, cctp_mint_recipient: Pubkey, - source_chain: Chain, - destination_chain: Chain, - source_address: [u8; 32], - destination_address: [u8; 32], + source_chain_and_address: ChainAndAddress, + destination_chain_and_address: ChainAndAddress, vaa_args: VaaArgs, ) -> TestVaaPairs { let mut test_fast_transfers = TestVaaPairs::new(); @@ -596,10 +609,8 @@ pub fn create_vaas_test_with_chain_and_address( program_test, mint_address, cctp_mint_recipient, - source_chain, - destination_chain, - source_address, - destination_address, + source_chain_and_address, + destination_chain_and_address, &vaa_args, ); test_fast_transfers @@ -636,7 +647,7 @@ impl EvmAddress { } pub fn from_hex(hex: &str) -> Option { - let hex = hex.strip_prefix("0x").unwrap_or(hex); + let hex = hex.strip_prefix("0x").unwrap_or_else(|| hex); let bytes = hex::decode(hex).ok()?; if bytes.len() != 20 { return None; From 3caf9633a4765787f9def4155e886b08c68688be Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 20:41:39 +0000 Subject: [PATCH 037/112] check idl fixes --- .../matching-engine/src/composite/mod.rs | 9 - solana/programs/matching-engine/src/lib.rs | 1 - .../auction/offer/place_initial/cctp_shim.rs | 223 --------- .../auction/offer/place_initial/mod.rs | 2 - solana/ts/src/idl/json/matching_engine.json | 439 ++---------------- solana/ts/src/idl/ts/matching_engine.ts | 439 ++---------------- 6 files changed, 62 insertions(+), 1051 deletions(-) delete mode 100644 solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs diff --git a/solana/programs/matching-engine/src/composite/mod.rs b/solana/programs/matching-engine/src/composite/mod.rs index f3507f0f8..d993d87f9 100644 --- a/solana/programs/matching-engine/src/composite/mod.rs +++ b/solana/programs/matching-engine/src/composite/mod.rs @@ -258,15 +258,6 @@ pub struct LiveRouterPath<'info> { pub to_endpoint: LiveRouterEndpoint<'info>, } -// TODO: Add a composite called FastOrderPathShim with two accounts: Guardian Set and Guardian Set Signatures -// Call verify hash on the instruction on the verify shim program -#[derive(Accounts)] -pub struct FastOrderPathShim<'info> { - pub guardian_set: UncheckedAccount<'info>, - pub guardian_set_signatures: UncheckedAccount<'info>, - pub live_router_path: LiveRouterPath<'info>, -} - #[derive(Accounts)] pub struct FastOrderPath<'info> { #[account( diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index e4131033a..2f2f68bd1 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -11,7 +11,6 @@ mod events; mod processor; pub use processor::CctpMessageArgs; pub use processor::InitializeArgs; -pub use processor::VaaMessage; use processor::*; pub mod state; diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs deleted file mode 100644 index 986af0166..000000000 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp_shim.rs +++ /dev/null @@ -1,223 +0,0 @@ -use crate::{ - composite::*, - error::MatchingEngineError, - state::{Auction, AuctionConfig, FastMarketOrder as FastMarketOrderState}, -}; -use anchor_lang::prelude::*; -use anchor_spl::token; -use common::TRANSFER_AUTHORITY_SEED_PREFIX; -use solana_program::keccak; - -#[derive(Accounts)] -#[instruction(offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage)] -pub struct PlaceInitialOfferCctpShim<'info> { - #[account(mut)] - payer: Signer<'info>, - - /// The auction participant needs to set approval to this PDA. - /// - /// CHECK: Seeds must be \["transfer-authority", auction.key(), offer_price.to_be_bytes()\]. - #[account( - seeds = [ - TRANSFER_AUTHORITY_SEED_PREFIX, - auction.key().as_ref(), - &offer_price.to_be_bytes() - ], - bump - )] - transfer_authority: UncheckedAccount<'info>, - - /// NOTE: This account is only used to pause inbound auctions. - #[account(constraint = !custodian.paused @ MatchingEngineError::Paused)] - custodian: CheckedCustodian<'info>, - - #[account( - constraint = { - require_eq!( - auction_config.id, - custodian.auction_config_id, - MatchingEngineError::AuctionConfigMismatch, - ); - - true - } - )] - auction_config: Account<'info, AuctionConfig>, - - /// The cpi instruction will verify the hash of the fast order path so no account constraints are needed. - fast_order_path_shim: FastOrderPathShim<'info>, - - #[account( - init, - payer = payer, - space = 8_usize.saturating_add(std::mem::size_of::()), - // │ └─ FastMarketOrderState account data size - // └─ Anchor discriminator (8 bytes) - seeds = [ - FastMarketOrderState::SEED_PREFIX, - vaa_message.digest().as_ref(), - // TODO: consider different seed - ], - bump - )] - fast_market_order: AccountLoader<'info, FastMarketOrderState>, - - /// This account should only be created once, and should never be changed to - /// init_if_needed. Otherwise someone can game an existing auction. - #[account( - init, - payer = payer, - space = 8 + Auction::INIT_SPACE, - seeds = [ - Auction::SEED_PREFIX, - vaa_message.digest().as_ref(), - ], - bump - )] - auction: Box>, - - #[account(mut)] - offer_token: Box>, - - #[account( - init, - payer = payer, - token::mint = usdc, - token::authority = auction, - seeds = [ - crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction.key().as_ref(), - ], - bump, - )] - auction_custody_token: Box>, - - usdc: Usdc<'info>, - - #[account(constraint = { - require_eq!( - verify_vaa_shim_program.key(), - wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - MatchingEngineError::InvalidVerifyVaaShimProgram - ); - - true - })] - verify_vaa_shim_program: UncheckedAccount<'info>, - system_program: Program<'info, System>, - token_program: Program<'info, token::Token>, -} - -// TODO: Change this to be PlaceInitialOfferArgs and go from there ... -/// A vaa message is the serialised message body of a posted vaa. Only the fields that are required to create the digest are included. -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct VaaMessage(pub Vec); - -impl VaaMessage { - pub fn new( - consistency_level: u8, - vaa_time: u32, - sequence: u64, - emitter_chain: u16, - emitter_address: [u8; 32], - payload: Vec, - ) -> Self { - Self( - VaaMessageBody::new( - consistency_level, - vaa_time, - sequence, - emitter_chain, - emitter_address, - payload, - ) - .to_vec(), - ) - } - - pub fn from_vec(vec: Vec) -> Self { - Self(vec) - } - - fn message_hash(&self) -> keccak::Hash { - keccak::hashv(&[self.0.as_ref()]) - } - - pub fn digest(&self) -> keccak::Hash { - keccak::hashv(&[self.message_hash().as_ref()]) - } - - #[allow(dead_code)] - fn nonce(&self) -> u32 { - // nonce is the next 4 bytes of the message - u32::from_be_bytes(self.0[4..8].try_into().unwrap()) - } - - pub fn emitter_chain(&self) -> u16 { - // emitter_chain is the next 2 bytes of the message - u16::from_be_bytes(self.0[8..10].try_into().unwrap()) - } - - pub fn emitter_address(&self) -> [u8; 32] { - // emitter_address is the next 32 bytes of the message - self.0[10..42].try_into().unwrap() - } -} - -/// Just a helper struct to make the code more readable. -struct VaaMessageBody { - /// Level of consistency requested by the emitter - pub consistency_level: u8, - - /// Time the vaa was submitted - pub vaa_time: u32, - - /// Unique nonce for this message - pub nonce: u32, - - /// Sequence number of this message - pub sequence: u64, - - /// Emitter of the message - pub emitter_chain: u16, - - /// Emitter of the message - pub emitter_address: [u8; 32], - - /// Message payload - pub payload: Vec, -} - -impl VaaMessageBody { - pub fn new( - consistency_level: u8, - vaa_time: u32, - sequence: u64, - emitter_chain: u16, - emitter_address: [u8; 32], - payload: Vec, - ) -> Self { - Self { - consistency_level, - vaa_time, - nonce: 0, // Always 0 - sequence, - emitter_chain, // Can be taken from the live router path - emitter_address, // Can be taken from the live router path - payload, - } - } - - fn to_vec(&self) -> Vec { - [ - self.vaa_time.to_be_bytes().as_ref(), - self.nonce.to_be_bytes().as_ref(), - self.emitter_chain.to_be_bytes().as_ref(), - &self.emitter_address, - &self.sequence.to_be_bytes(), - &[self.consistency_level], - self.payload.as_ref(), - ] - .concat() - } -} diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs index baa4bb73e..c143ed248 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/mod.rs @@ -1,4 +1,2 @@ mod cctp; pub use cctp::*; -mod cctp_shim; -pub use cctp_shim::*; diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index dd46e1097..07631ed96 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -1,5 +1,5 @@ { - "address": "MatchingEngine11111111111111111111111111111", + "address": "", "metadata": { "name": "matching_engine", "version": "0.0.0", @@ -961,9 +961,35 @@ ], "args": [] }, + { + "name": "get_cctp_mint_recipient", + "docs": [ + "UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol." + ], + "discriminator": [ + 244, + 239, + 207, + 186, + 19, + 125, + 44, + 181 + ], + "accounts": [ + { + "name": "mint_recipient", + "writable": true + } + ], + "args": [] + }, { "name": "improve_offer", "docs": [ + "This instruction is used to create a new auction given a valid `VaaShim`.", + "This instruction should act in the exact same way as `place_initial_offer_cctp` except that", + "it will check the digest of the vaa directly using a cpi call to the verify shim program.", "This instruction is used to improve an existing auction offer. The `offer_price` must be", "greater than the current `offer_price` in the auction. This instruction will revert if the", "`offer_price` is less than the current `offer_price`. This instruction can be called by", @@ -1014,7 +1040,8 @@ ] }, { - "name": "offer_token" + "name": "offer_token", + "writable": true }, { "name": "token_program" @@ -1269,7 +1296,8 @@ "writable": true }, { - "name": "offer_token" + "name": "offer_token", + "writable": true }, { "name": "auction_custody_token", @@ -2706,411 +2734,6 @@ ] } ], - "events": [ - { - "name": "AuctionClosed", - "discriminator": [ - 104, - 72, - 168, - 177, - 241, - 79, - 231, - 167 - ] - }, - { - "name": "AuctionSettled", - "discriminator": [ - 61, - 151, - 131, - 170, - 95, - 203, - 219, - 147 - ] - }, - { - "name": "AuctionUpdated", - "discriminator": [ - 67, - 35, - 50, - 236, - 108, - 230, - 253, - 111 - ] - }, - { - "name": "Enacted", - "discriminator": [ - 200, - 226, - 146, - 0, - 188, - 24, - 141, - 143 - ] - }, - { - "name": "FastFillRedeemed", - "discriminator": [ - 192, - 96, - 201, - 180, - 102, - 112, - 34, - 102 - ] - }, - { - "name": "FastFillSequenceReserved", - "discriminator": [ - 6, - 154, - 159, - 87, - 13, - 183, - 211, - 152 - ] - }, - { - "name": "LocalFastOrderFilled", - "discriminator": [ - 131, - 247, - 217, - 194, - 154, - 179, - 238, - 193 - ] - }, - { - "name": "OrderExecuted", - "discriminator": [ - 74, - 135, - 231, - 5, - 168, - 106, - 194, - 117 - ] - }, - { - "name": "Proposed", - "discriminator": [ - 216, - 37, - 138, - 141, - 130, - 208, - 180, - 153 - ] - } - ], - "errors": [ - { - "code": 6002, - "name": "OwnerOnly" - }, - { - "code": 6004, - "name": "OwnerOrAssistantOnly" - }, - { - "code": 6016, - "name": "U64Overflow" - }, - { - "code": 6018, - "name": "U32Overflow" - }, - { - "code": 6032, - "name": "SameEndpoint" - }, - { - "code": 6034, - "name": "InvalidEndpoint" - }, - { - "code": 6048, - "name": "InvalidVaa" - }, - { - "code": 6066, - "name": "InvalidDeposit" - }, - { - "code": 6068, - "name": "InvalidDepositMessage" - }, - { - "code": 6070, - "name": "InvalidPayloadId" - }, - { - "code": 6072, - "name": "InvalidDepositPayloadId" - }, - { - "code": 6074, - "name": "NotFastMarketOrder" - }, - { - "code": 6076, - "name": "VaaMismatch" - }, - { - "code": 6078, - "name": "RedeemerMessageTooLarge" - }, - { - "code": 6096, - "name": "InvalidSourceRouter" - }, - { - "code": 6098, - "name": "InvalidTargetRouter" - }, - { - "code": 6100, - "name": "EndpointDisabled" - }, - { - "code": 6102, - "name": "InvalidCctpEndpoint" - }, - { - "code": 6128, - "name": "Paused" - }, - { - "code": 6256, - "name": "AssistantZeroPubkey" - }, - { - "code": 6257, - "name": "FeeRecipientZeroPubkey" - }, - { - "code": 6258, - "name": "ImmutableProgram" - }, - { - "code": 6260, - "name": "ZeroDuration" - }, - { - "code": 6262, - "name": "ZeroGracePeriod" - }, - { - "code": 6263, - "name": "ZeroPenaltyPeriod" - }, - { - "code": 6264, - "name": "UserPenaltyRewardBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6266, - "name": "InitialPenaltyBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6268, - "name": "MinOfferDeltaBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6270, - "name": "ZeroSecurityDepositBase" - }, - { - "code": 6271, - "name": "SecurityDepositBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6514, - "name": "InvalidNewOwner" - }, - { - "code": 6516, - "name": "AlreadyOwner" - }, - { - "code": 6518, - "name": "NoTransferOwnershipRequest" - }, - { - "code": 6520, - "name": "NotPendingOwner" - }, - { - "code": 6524, - "name": "InvalidChain" - }, - { - "code": 6576, - "name": "ChainNotAllowed" - }, - { - "code": 6578, - "name": "InvalidMintRecipient" - }, - { - "code": 6768, - "name": "ProposalAlreadyEnacted" - }, - { - "code": 6770, - "name": "ProposalDelayNotExpired" - }, - { - "code": 6772, - "name": "InvalidProposal" - }, - { - "code": 6832, - "name": "AuctionConfigMismatch" - }, - { - "code": 7024, - "name": "FastMarketOrderExpired" - }, - { - "code": 7026, - "name": "OfferPriceTooHigh" - }, - { - "code": 7032, - "name": "AuctionNotActive" - }, - { - "code": 7034, - "name": "AuctionPeriodExpired" - }, - { - "code": 7036, - "name": "AuctionPeriodNotExpired" - }, - { - "code": 7044, - "name": "ExecutorTokenMismatch" - }, - { - "code": 7050, - "name": "AuctionNotCompleted" - }, - { - "code": 7054, - "name": "CarpingNotAllowed" - }, - { - "code": 7056, - "name": "AuctionNotSettled" - }, - { - "code": 7058, - "name": "ExecutorNotPreparedBy" - }, - { - "code": 7060, - "name": "InvalidOfferToken" - }, - { - "code": 7062, - "name": "FastFillTooLarge" - }, - { - "code": 7064, - "name": "AuctionExists" - }, - { - "code": 7065, - "name": "NoAuction" - }, - { - "code": 7066, - "name": "BestOfferTokenMismatch" - }, - { - "code": 7068, - "name": "BestOfferTokenRequired" - }, - { - "code": 7070, - "name": "PreparedByMismatch" - }, - { - "code": 7071, - "name": "PreparedOrderResponseNotRequired" - }, - { - "code": 7072, - "name": "AuctionConfigNotRequired" - }, - { - "code": 7073, - "name": "BestOfferTokenNotRequired" - }, - { - "code": 7076, - "name": "FastFillAlreadyRedeemed" - }, - { - "code": 7077, - "name": "FastFillNotRedeemed" - }, - { - "code": 7080, - "name": "ReservedSequenceMismatch" - }, - { - "code": 7082, - "name": "AuctionAlreadySettled" - }, - { - "code": 7084, - "name": "InvalidBaseFeeToken" - }, - { - "code": 7086, - "name": "BaseFeeTokenRequired" - }, - { - "code": 7280, - "name": "CannotCloseAuctionYet" - }, - { - "code": 7282, - "name": "AuctionHistoryNotFull" - }, - { - "code": 7284, - "name": "AuctionHistoryFull" - } - ], "types": [ { "name": "AddCctpRouterEndpointArgs", diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 2d0af2e66..7f65146b3 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -5,7 +5,7 @@ * IDL can be found at `target/idl/matching_engine.json`. */ export type MatchingEngine = { - "address": "MatchingEngine11111111111111111111111111111", + "address": "", "metadata": { "name": "matchingEngine", "version": "0.0.0", @@ -967,9 +967,35 @@ export type MatchingEngine = { ], "args": [] }, + { + "name": "getCctpMintRecipient", + "docs": [ + "UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol." + ], + "discriminator": [ + 244, + 239, + 207, + 186, + 19, + 125, + 44, + 181 + ], + "accounts": [ + { + "name": "mintRecipient", + "writable": true + } + ], + "args": [] + }, { "name": "improveOffer", "docs": [ + "This instruction is used to create a new auction given a valid `VaaShim`.", + "This instruction should act in the exact same way as `place_initial_offer_cctp` except that", + "it will check the digest of the vaa directly using a cpi call to the verify shim program.", "This instruction is used to improve an existing auction offer. The `offer_price` must be", "greater than the current `offer_price` in the auction. This instruction will revert if the", "`offer_price` is less than the current `offer_price`. This instruction can be called by", @@ -1020,7 +1046,8 @@ export type MatchingEngine = { ] }, { - "name": "offerToken" + "name": "offerToken", + "writable": true }, { "name": "tokenProgram" @@ -1275,7 +1302,8 @@ export type MatchingEngine = { "writable": true }, { - "name": "offerToken" + "name": "offerToken", + "writable": true }, { "name": "auctionCustodyToken", @@ -2712,411 +2740,6 @@ export type MatchingEngine = { ] } ], - "events": [ - { - "name": "auctionClosed", - "discriminator": [ - 104, - 72, - 168, - 177, - 241, - 79, - 231, - 167 - ] - }, - { - "name": "auctionSettled", - "discriminator": [ - 61, - 151, - 131, - 170, - 95, - 203, - 219, - 147 - ] - }, - { - "name": "auctionUpdated", - "discriminator": [ - 67, - 35, - 50, - 236, - 108, - 230, - 253, - 111 - ] - }, - { - "name": "enacted", - "discriminator": [ - 200, - 226, - 146, - 0, - 188, - 24, - 141, - 143 - ] - }, - { - "name": "fastFillRedeemed", - "discriminator": [ - 192, - 96, - 201, - 180, - 102, - 112, - 34, - 102 - ] - }, - { - "name": "fastFillSequenceReserved", - "discriminator": [ - 6, - 154, - 159, - 87, - 13, - 183, - 211, - 152 - ] - }, - { - "name": "localFastOrderFilled", - "discriminator": [ - 131, - 247, - 217, - 194, - 154, - 179, - 238, - 193 - ] - }, - { - "name": "orderExecuted", - "discriminator": [ - 74, - 135, - 231, - 5, - 168, - 106, - 194, - 117 - ] - }, - { - "name": "proposed", - "discriminator": [ - 216, - 37, - 138, - 141, - 130, - 208, - 180, - 153 - ] - } - ], - "errors": [ - { - "code": 6002, - "name": "ownerOnly" - }, - { - "code": 6004, - "name": "ownerOrAssistantOnly" - }, - { - "code": 6016, - "name": "u64Overflow" - }, - { - "code": 6018, - "name": "u32Overflow" - }, - { - "code": 6032, - "name": "sameEndpoint" - }, - { - "code": 6034, - "name": "invalidEndpoint" - }, - { - "code": 6048, - "name": "invalidVaa" - }, - { - "code": 6066, - "name": "invalidDeposit" - }, - { - "code": 6068, - "name": "invalidDepositMessage" - }, - { - "code": 6070, - "name": "invalidPayloadId" - }, - { - "code": 6072, - "name": "invalidDepositPayloadId" - }, - { - "code": 6074, - "name": "notFastMarketOrder" - }, - { - "code": 6076, - "name": "vaaMismatch" - }, - { - "code": 6078, - "name": "redeemerMessageTooLarge" - }, - { - "code": 6096, - "name": "invalidSourceRouter" - }, - { - "code": 6098, - "name": "invalidTargetRouter" - }, - { - "code": 6100, - "name": "endpointDisabled" - }, - { - "code": 6102, - "name": "invalidCctpEndpoint" - }, - { - "code": 6128, - "name": "paused" - }, - { - "code": 6256, - "name": "assistantZeroPubkey" - }, - { - "code": 6257, - "name": "feeRecipientZeroPubkey" - }, - { - "code": 6258, - "name": "immutableProgram" - }, - { - "code": 6260, - "name": "zeroDuration" - }, - { - "code": 6262, - "name": "zeroGracePeriod" - }, - { - "code": 6263, - "name": "zeroPenaltyPeriod" - }, - { - "code": 6264, - "name": "userPenaltyRewardBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6266, - "name": "initialPenaltyBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6268, - "name": "minOfferDeltaBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6270, - "name": "zeroSecurityDepositBase" - }, - { - "code": 6271, - "name": "securityDepositBpsTooLarge", - "msg": "Value exceeds 1000000" - }, - { - "code": 6514, - "name": "invalidNewOwner" - }, - { - "code": 6516, - "name": "alreadyOwner" - }, - { - "code": 6518, - "name": "noTransferOwnershipRequest" - }, - { - "code": 6520, - "name": "notPendingOwner" - }, - { - "code": 6524, - "name": "invalidChain" - }, - { - "code": 6576, - "name": "chainNotAllowed" - }, - { - "code": 6578, - "name": "invalidMintRecipient" - }, - { - "code": 6768, - "name": "proposalAlreadyEnacted" - }, - { - "code": 6770, - "name": "proposalDelayNotExpired" - }, - { - "code": 6772, - "name": "invalidProposal" - }, - { - "code": 6832, - "name": "auctionConfigMismatch" - }, - { - "code": 7024, - "name": "fastMarketOrderExpired" - }, - { - "code": 7026, - "name": "offerPriceTooHigh" - }, - { - "code": 7032, - "name": "auctionNotActive" - }, - { - "code": 7034, - "name": "auctionPeriodExpired" - }, - { - "code": 7036, - "name": "auctionPeriodNotExpired" - }, - { - "code": 7044, - "name": "executorTokenMismatch" - }, - { - "code": 7050, - "name": "auctionNotCompleted" - }, - { - "code": 7054, - "name": "carpingNotAllowed" - }, - { - "code": 7056, - "name": "auctionNotSettled" - }, - { - "code": 7058, - "name": "executorNotPreparedBy" - }, - { - "code": 7060, - "name": "invalidOfferToken" - }, - { - "code": 7062, - "name": "fastFillTooLarge" - }, - { - "code": 7064, - "name": "auctionExists" - }, - { - "code": 7065, - "name": "noAuction" - }, - { - "code": 7066, - "name": "bestOfferTokenMismatch" - }, - { - "code": 7068, - "name": "bestOfferTokenRequired" - }, - { - "code": 7070, - "name": "preparedByMismatch" - }, - { - "code": 7071, - "name": "preparedOrderResponseNotRequired" - }, - { - "code": 7072, - "name": "auctionConfigNotRequired" - }, - { - "code": 7073, - "name": "bestOfferTokenNotRequired" - }, - { - "code": 7076, - "name": "fastFillAlreadyRedeemed" - }, - { - "code": 7077, - "name": "fastFillNotRedeemed" - }, - { - "code": 7080, - "name": "reservedSequenceMismatch" - }, - { - "code": 7082, - "name": "auctionAlreadySettled" - }, - { - "code": 7084, - "name": "invalidBaseFeeToken" - }, - { - "code": 7086, - "name": "baseFeeTokenRequired" - }, - { - "code": 7280, - "name": "cannotCloseAuctionYet" - }, - { - "code": 7282, - "name": "auctionHistoryNotFull" - }, - { - "code": 7284, - "name": "auctionHistoryFull" - } - ], "types": [ { "name": "addCctpRouterEndpointArgs", From 573b4778236fef83bdf107cc5e763d93af40b661 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 21:23:28 +0000 Subject: [PATCH 038/112] any changes here? --- solana/ts/tests/01__matchingEngine.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solana/ts/tests/01__matchingEngine.ts b/solana/ts/tests/01__matchingEngine.ts index 3c9775aef..d4bba7a5b 100644 --- a/solana/ts/tests/01__matchingEngine.ts +++ b/solana/ts/tests/01__matchingEngine.ts @@ -3972,12 +3972,11 @@ describe("Matching Engine", function () { if (!ix.programId.equals(engine.ID) || !("data" in ix)) { continue; } - const data = utils.bytes.bs58.decode(ix.data); if (!data.subarray(0, 8).equals(CPI_EVENT_IX_SELECTOR)) { continue; } - + console.log("data", data); const decoded = engine.program.coder.events.decode( utils.bytes.base64.encode(data.subarray(8)), ); From d3c9ea5ac508bcdc559c40fc6817b367ed4ce118 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 26 Mar 2025 22:08:47 +0000 Subject: [PATCH 039/112] trying to unexpose composite --- solana/programs/matching-engine/src/lib.rs | 17 ++++---------- solana/ts/src/idl/json/matching_engine.json | 26 --------------------- solana/ts/src/idl/ts/matching_engine.ts | 26 --------------------- 3 files changed, 5 insertions(+), 64 deletions(-) diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index 2f2f68bd1..e7cf722c2 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -2,7 +2,7 @@ #![allow(clippy::result_large_err)] mod composite; -use composite::*; +// use composite::*; pub mod error; @@ -258,13 +258,6 @@ pub mod matching_engine { processor::place_initial_offer_cctp(ctx, offer_price) } - /// This instruction is used to create a new auction given a valid `VaaShim`. - /// This instruction should act in the exact same way as `place_initial_offer_cctp` except that - /// it will check the digest of the vaa directly using a cpi call to the verify shim program. - // pub fn place_initial_offer_cctp_shim(ctx: Context, offer_price: u64, guardian_set_bump: u8, vaa_message: VaaMessage) -> Result<()> { - // processor::place_initial_offer_cctp_shim(ctx, offer_price, guardian_set_bump, vaa_message) - // } - /// This instruction is used to improve an existing auction offer. The `offer_price` must be /// greater than the current `offer_price` in the auction. This instruction will revert if the /// `offer_price` is less than the current `offer_price`. This instruction can be called by @@ -486,10 +479,10 @@ pub mod matching_engine { err!(ErrorCode::Deprecated) } - /// UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol. - pub fn get_cctp_mint_recipient(_ctx: Context) -> Result<()> { - err!(ErrorCode::InstructionMissing) - } + /// UNUSED. This instruction does not exist and has never existed. It just reverts and exist to expose an account lol. + // pub fn get_cctp_mint_recipient(_ctx: Context) -> Result<()> { + // err!(ErrorCode::InstructionMissing) + // } /// Non anchor function for placing an initial offer using the VAA shim. pub fn fallback_process_instruction( diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index 07631ed96..3b3c04075 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -961,35 +961,9 @@ ], "args": [] }, - { - "name": "get_cctp_mint_recipient", - "docs": [ - "UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol." - ], - "discriminator": [ - 244, - 239, - 207, - 186, - 19, - 125, - 44, - 181 - ], - "accounts": [ - { - "name": "mint_recipient", - "writable": true - } - ], - "args": [] - }, { "name": "improve_offer", "docs": [ - "This instruction is used to create a new auction given a valid `VaaShim`.", - "This instruction should act in the exact same way as `place_initial_offer_cctp` except that", - "it will check the digest of the vaa directly using a cpi call to the verify shim program.", "This instruction is used to improve an existing auction offer. The `offer_price` must be", "greater than the current `offer_price` in the auction. This instruction will revert if the", "`offer_price` is less than the current `offer_price`. This instruction can be called by", diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 7f65146b3..019ce75bb 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -967,35 +967,9 @@ export type MatchingEngine = { ], "args": [] }, - { - "name": "getCctpMintRecipient", - "docs": [ - "UNUSED. This instruction does not exist anymore. It just reverts and exist to expose an account lol." - ], - "discriminator": [ - 244, - 239, - 207, - 186, - 19, - 125, - 44, - 181 - ], - "accounts": [ - { - "name": "mintRecipient", - "writable": true - } - ], - "args": [] - }, { "name": "improveOffer", "docs": [ - "This instruction is used to create a new auction given a valid `VaaShim`.", - "This instruction should act in the exact same way as `place_initial_offer_cctp` except that", - "it will check the digest of the vaa directly using a cpi call to the verify shim program.", "This instruction is used to improve an existing auction offer. The `offer_price` must be", "greater than the current `offer_price` in the auction. This instruction will revert if the", "`offer_price` is less than the current `offer_price`. This instruction can be called by", From 93addf30e0a218bfc2b5a29ab9880a2dafb6f918 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 27 Mar 2025 16:28:52 +0000 Subject: [PATCH 040/112] improved place initial offer shim --- .../fallback/processor/place_initial_offer.rs | 26 +++++-------------- solana/programs/matching-engine/src/lib.rs | 10 +++---- .../tests/shimful/shims_make_offer.rs | 7 +---- .../tests/testing_engine/config.rs | 2 ++ .../tests/testing_engine/engine.rs | 9 +++++-- .../matching-engine/tests/utils/constants.rs | 23 ++++++++-------- .../matching-engine/tests/utils/router.rs | 8 +++--- .../matching-engine/tests/utils/vaa.rs | 5 ++-- 8 files changed, 40 insertions(+), 50 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 83074a5c8..4b1bd989e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -22,21 +22,11 @@ use crate::error::MatchingEngineError; #[repr(C)] pub struct PlaceInitialOfferCctpShimData { pub offer_price: u64, - pub sequence: u64, - pub vaa_time: u32, - pub consistency_level: u8, - _padding: [u8; 3], } impl PlaceInitialOfferCctpShimData { - pub fn new(offer_price: u64, sequence: u64, vaa_time: u32, consistency_level: u8) -> Self { - Self { - offer_price, - sequence, - vaa_time, - consistency_level, - _padding: [0_u8; 3], - } + pub fn new(offer_price: u64) -> Self { + Self { offer_price } } pub fn from_bytes(data: &[u8]) -> Option<&Self> { @@ -193,13 +183,7 @@ pub fn place_initial_offer_cctp_shim( check_account_length(accounts, 11)?; // Extract data fields // TODO: Remove sequence, vaa_time because they are in the fast market order state - let PlaceInitialOfferCctpShimData { - offer_price, - sequence, - vaa_time, - consistency_level, - _padding, - } = *data; + let PlaceInitialOfferCctpShimData { offer_price } = *data; let signer = &accounts[0]; let transfer_authority = &accounts[1]; @@ -217,6 +201,10 @@ pub fn place_initial_offer_cctp_shim( let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; + let vaa_time = fast_market_order_zero_copy.vaa_timestamp; + let sequence = fast_market_order_zero_copy.vaa_sequence; + let consistency_level = fast_market_order_zero_copy.vaa_consistency_level; + // Check pda of the transfer authority is valid let transfer_authority_seeds = [ TRANSFER_AUTHORITY_SEED_PREFIX, diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index e7cf722c2..b1cd3e6d1 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -2,9 +2,9 @@ #![allow(clippy::result_large_err)] mod composite; -// use composite::*; +use composite::*; -pub mod error; +mod error; mod events; @@ -480,9 +480,9 @@ pub mod matching_engine { } /// UNUSED. This instruction does not exist and has never existed. It just reverts and exist to expose an account lol. - // pub fn get_cctp_mint_recipient(_ctx: Context) -> Result<()> { - // err!(ErrorCode::InstructionMissing) - // } + pub fn get_cctp_mint_recipient(_ctx: Context) -> Result<()> { + err!(ErrorCode::InstructionMissing) + } /// Non anchor function for placing an initial offer using the VAA shim. pub fn fallback_process_instruction( diff --git a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs index 685332c89..4763ea5cd 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs @@ -82,12 +82,7 @@ pub async fn place_initial_offer_fallback( let solver_usdc_balance_before = solver.get_balance(test_ctx).await; - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new( - offer_price, - vaa_data.sequence, - vaa_data.vaa_time, - vaa_data.consistency_level, - ); + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price); let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { signer: &payer_signer.pubkey(), diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/programs/matching-engine/tests/testing_engine/config.rs index 634ec19fc..603c88aa6 100644 --- a/solana/programs/matching-engine/tests/testing_engine/config.rs +++ b/solana/programs/matching-engine/tests/testing_engine/config.rs @@ -26,6 +26,7 @@ pub struct InitializeInstructionConfig { pub struct CreateCctpRouterEndpointsInstructionConfig { pub chains: HashSet, + pub admin_owner_or_assistant: Option>, pub expected_error: Option, } @@ -33,6 +34,7 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { fn default() -> Self { Self { chains: HashSet::from([Chain::Ethereum, Chain::Arbitrum, Chain::Solana]), + admin_owner_or_assistant: None, expected_error: None, } } diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 7658a4d23..1e903a844 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -17,6 +17,7 @@ use crate::utils::{ setup::TestingContext, }; use anchor_lang::prelude::*; +use solana_sdk::signature::Signer; #[allow(dead_code)] pub enum InstructionTrigger { @@ -209,11 +210,15 @@ impl TestingEngine { .expect("Testing state is not initialized"); let custodian_address = initialized_state.custodian_address; let testing_actors = &self.testing_context.testing_actors; + let admin_owner_or_assistant = config + .admin_owner_or_assistant + .clone() + .unwrap_or_else(|| testing_actors.owner.keypair()); let result = create_all_router_endpoints_test( &self.testing_context, - testing_actors.owner.pubkey(), + admin_owner_or_assistant.pubkey(), custodian_address, - testing_actors.owner.keypair(), + admin_owner_or_assistant, config.chains.clone(), ) .await; diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/programs/matching-engine/tests/utils/constants.rs index f25f5c057..687df52dc 100644 --- a/solana/programs/matching-engine/tests/utils/constants.rs +++ b/solana/programs/matching-engine/tests/utils/constants.rs @@ -115,17 +115,6 @@ pub fn get_player_one_keypair() -> Keypair { pub const ETHEREUM_USDC_ADDRESS: &str = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; -// Chain to cctp domain mapping -pub const CHAIN_TO_DOMAIN: &[(Chain, u32)] = &[ - (Chain::Ethereum, 0), - (Chain::Avalanche, 1), - (Chain::Optimism, 2), - (Chain::Arbitrum, 3), - (Chain::Solana, 5), - (Chain::Base, 6), - (Chain::Polygon, 7), -]; - // Enum for Chain types #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Chain { @@ -150,6 +139,18 @@ impl Chain { Chain::Polygon => 6, } } + + pub fn as_cctp_domain(&self) -> u32 { + match self { + Chain::Ethereum => 0, + Chain::Avalanche => 1, + Chain::Optimism => 2, + Chain::Arbitrum => 3, + Chain::Solana => 5, + Chain::Base => 6, + Chain::Polygon => 7, + } + } } // Registered Token Routers diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/programs/matching-engine/tests/utils/router.rs index 8567a7cbd..a198acf20 100644 --- a/solana/programs/matching-engine/tests/utils/router.rs +++ b/solana/programs/matching-engine/tests/utils/router.rs @@ -189,7 +189,7 @@ pub async fn add_cctp_router_endpoint_ix( let ix_data = AddCctpRouterEndpoint { args: AddCctpRouterEndpointArgs { chain: chain.as_chain_id(), - cctp_domain: CHAIN_TO_DOMAIN[chain.as_index()].1, + cctp_domain: chain.as_cctp_domain(), address: registered_token_router_address, mint_recipient: None, }, @@ -237,7 +237,7 @@ pub async fn add_cctp_router_endpoint_ix( &Pubkey::new_from_array(registered_token_router_address), None, matching_engine::state::MessageProtocol::Cctp { - domain: CHAIN_TO_DOMAIN[chain.as_index()].1, + domain: chain.as_cctp_domain(), }, ); test_router_endpoint @@ -334,7 +334,7 @@ pub async fn create_cctp_router_endpoint( ) -> TestRouterEndpoint { let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); let program_id = testing_context.get_matching_engine_program_id(); - let token_messenger = match chain { + let remote_token_messenger = match chain { Chain::Arbitrum => fixture_accounts.arbitrum_remote_token_messenger, Chain::Ethereum => fixture_accounts.ethereum_remote_token_messenger, _ => { @@ -348,7 +348,7 @@ pub async fn create_cctp_router_endpoint( custodian_address, admin_keypair.as_ref(), program_id, - token_messenger, + remote_token_messenger, chain, ) .await diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/programs/matching-engine/tests/utils/vaa.rs index 62bfe3052..a39d82e8f 100644 --- a/solana/programs/matching-engine/tests/utils/vaa.rs +++ b/solana/programs/matching-engine/tests/utils/vaa.rs @@ -7,7 +7,6 @@ use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use super::constants::Chain; use super::setup::TestingContext; -use super::CHAIN_TO_DOMAIN; use super::constants::CORE_BRIDGE_PID; use borsh::{BorshDeserialize, BorshSerialize}; @@ -409,8 +408,8 @@ pub fn create_deposit_message( let deposit = Deposit { token_address: token_mint.to_bytes(), amount: ruint::aliases::U256::from(amount), - source_cctp_domain: CHAIN_TO_DOMAIN[source_address.chain.as_index()].1, - destination_cctp_domain: CHAIN_TO_DOMAIN[Chain::Solana.as_index()].1, // Hardcode solana as destination domain + source_cctp_domain: source_address.chain.as_cctp_domain(), + destination_cctp_domain: Chain::Solana.as_cctp_domain(), // Hardcode solana as destination domain cctp_nonce, burn_source: source_address.address.to_bytes(), // Token router address mint_recipient: cctp_mint_recipient.to_bytes(), // Mint recipient program id From 89220ca2b24d14fdc2aad0eb7debc21b43dd2d96 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 27 Mar 2025 17:02:40 +0000 Subject: [PATCH 041/112] execute order added as well --- .../src/fallback/processor/mod.rs | 4 +- .../fallback/processor/process_instruction.rs | 34 +-- .../tests/integration_tests.rs | 106 ++++---- .../matching-engine/tests/shimful/mod.rs | 2 +- .../tests/shimful/shims_execute_order.rs | 2 +- .../tests/shimless/execute_order.rs | 2 +- .../matching-engine/tests/shimless/mod.rs | 2 +- .../tests/testing_engine/engine.rs | 238 +++++++++--------- 8 files changed, 195 insertions(+), 195 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index f9e956cb6..a0e155c51 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,8 +1,8 @@ pub mod process_instruction; pub use process_instruction::*; -// pub mod burn_and_post; +pub mod burn_and_post; pub mod close_fast_market_order; -// pub mod execute_order; +pub mod execute_order; pub mod initialise_fast_market_order; pub mod place_initial_offer; // pub mod prepare_order_response; diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 9edc6ff96..860ead502 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -1,5 +1,5 @@ use super::close_fast_market_order::close_fast_market_order; -// use super::execute_order::handle_execute_order_shim; +use super::execute_order::handle_execute_order_shim; use super::initialise_fast_market_order::{ initialise_fast_market_order, InitialiseFastMarketOrderData, }; @@ -27,7 +27,7 @@ pub enum FallbackMatchingEngineInstruction<'ix> { InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), CloseFastMarketOrder, PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), - // ExecuteOrderCctpShim, + ExecuteOrderCctpShim, // PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } @@ -50,10 +50,10 @@ pub fn process_instruction( } FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, data) - } // FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { - // handle_execute_order_shim(accounts) - // } - // FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { + } + FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { + handle_execute_order_shim(accounts) + } // FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { // prepare_order_response_cctp_shim(accounts, data) // } } @@ -78,9 +78,9 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) } - // FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { - // Some(Self::ExecuteOrderCctpShim) - // } + FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { + Some(Self::ExecuteOrderCctpShim) + } // FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { // Some(Self::PrepareOrderResponseCctpShim( // PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), @@ -112,17 +112,17 @@ impl FallbackMatchingEngineInstruction<'_> { out } - // Self::ExecuteOrderCctpShim => { - // let total_capacity = 8; // 8 for the selector (no data) + Self::ExecuteOrderCctpShim => { + let total_capacity = 8; // 8 for the selector (no data) - // let mut out = Vec::with_capacity(total_capacity); + let mut out = Vec::with_capacity(total_capacity); - // out.extend_from_slice( - // &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, - // ); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, + ); - // out - // } + out + } Self::InitialiseFastMarketOrder(data) => { let data_slice = bytemuck::bytes_of(*data); let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/programs/matching-engine/tests/integration_tests.rs index f65492a6c..daf45eab5 100644 --- a/solana/programs/matching-engine/tests/integration_tests.rs +++ b/solana/programs/matching-engine/tests/integration_tests.rs @@ -358,60 +358,60 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { testing_engine.execute(instruction_triggers).await; } -// #[tokio::test] -// // Testing an execute order from arbitrum to ethereum -// // TODO: Flesh out this test to see if the message was posted correctly -// pub async fn test_execute_order_fallback() { -// let transfer_direction = TransferDirection::FromArbitrumToEthereum; -// let vaa_args = VaaArgs { -// post_vaa: false, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } +#[tokio::test] +// Testing an execute order from arbitrum to ethereum +// TODO: Flesh out this test to see if the message was posted correctly +pub async fn test_execute_order_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} -// #[tokio::test] -// pub async fn test_execute_order_shimless() { -// let transfer_direction = TransferDirection::FromArbitrumToEthereum; -// let vaa_args = VaaArgs { -// post_vaa: true, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } +#[tokio::test] +pub async fn test_execute_order_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} // pub async fn test_execute_order_fallback_blocks_shimless() { // let transfer_direction = TransferDirection::FromArbitrumToEthereum; // let vaa_args = VaaArgs { diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/programs/matching-engine/tests/shimful/mod.rs index 68a5fd8fa..6553af252 100644 --- a/solana/programs/matching-engine/tests/shimful/mod.rs +++ b/solana/programs/matching-engine/tests/shimful/mod.rs @@ -1,7 +1,7 @@ #![allow(clippy::expect_used)] pub mod fast_market_order_shim; pub mod post_message; -// pub mod shims_execute_order; +pub mod shims_execute_order; pub mod shims_make_offer; // pub mod shims_prepare_order_response; pub mod verify_shim; diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index aea079bcd..b4eb54770 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -182,7 +182,7 @@ pub async fn execute_order_fallback( // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; - utils::setup::fast_forward_slots(test_ctx, 3).await; + utils::setup::fast_forward_slots(&testing_context, 3).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs index 6ddf1f426..56b9d7bcb 100644 --- a/solana/programs/matching-engine/tests/shimless/execute_order.rs +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -158,7 +158,7 @@ pub async fn execute_order_shimless_test( payer_signer: &Rc, expected_error: Option<&ExpectedError>, ) -> Option { - crate::utils::setup::fast_forward_slots(&testing_context.test_context, 3).await; + crate::utils::setup::fast_forward_slots(&testing_context, 3).await; let fixture_accounts = testing_context .get_fixture_accounts() .expect("Fixture accounts not found"); diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/programs/matching-engine/tests/shimless/mod.rs index a949c85a9..5e02d8d2b 100644 --- a/solana/programs/matching-engine/tests/shimless/mod.rs +++ b/solana/programs/matching-engine/tests/shimless/mod.rs @@ -1,6 +1,6 @@ #![allow(clippy::expect_used)] -// pub mod execute_order; +pub mod execute_order; pub mod initialize; pub mod make_offer; // pub mod prepare_order_response; diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 1e903a844..09c3bde25 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -27,8 +27,8 @@ pub enum InstructionTrigger { PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), ImproveOfferShimless(ImproveOfferInstructionConfig), - // ExecuteOrderShimless(ExecuteOrderInstructionConfig), - // ExecuteOrderShim(ExecuteOrderInstructionConfig), + ExecuteOrderShimless(ExecuteOrderInstructionConfig), + ExecuteOrderShim(ExecuteOrderInstructionConfig), // PrepareOrderShimless(PrepareOrderInstructionConfig), // PrepareOrderShim(PrepareOrderInstructionConfig), // SettleAuction(SettleAuctionInstructionConfig), @@ -118,13 +118,13 @@ impl TestingEngine { } InstructionTrigger::ImproveOfferShimless(config) => { self.improve_offer_shimless(current_state, config).await - } // InstructionTrigger::ExecuteOrderShim(config) => { - // self.execute_order_shim(current_state, config).await - // } - // InstructionTrigger::ExecuteOrderShimless(config) => { - // self.execute_order_shimless(current_state, config).await - // } - // InstructionTrigger::PrepareOrderShim(config) => { + } + InstructionTrigger::ExecuteOrderShim(config) => { + self.execute_order_shim(current_state, config).await + } + InstructionTrigger::ExecuteOrderShimless(config) => { + self.execute_order_shimless(current_state, config).await + } // InstructionTrigger::PrepareOrderShim(config) => { // self.prepare_order_shim(current_state, config).await // } // InstructionTrigger::PrepareOrderShimless(config) => { @@ -525,118 +525,118 @@ impl TestingEngine { current_state.clone() } - // async fn execute_order_shim( - // &self, - // current_state: &TestingEngineState, - // config: &ExecuteOrderInstructionConfig, - // ) -> TestingEngineState { - // let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + async fn execute_order_shim( + &self, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, + ) -> TestingEngineState { + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - // // TODO: Change to get auction accounts from current state - // let auction_accounts = current_state - // .auction_accounts() - // .expect("Auction accounts not found"); - // let fast_market_order_address = config.fast_market_order_address.unwrap_or( - // current_state - // .fast_market_order() - // .expect("Fast market order is not created") - // .fast_market_order_address, - // ); - // let active_auction_state = current_state - // .auction_state() - // .get_active_auction() - // .expect("Active auction not found"); - // let result = shimful::shims_execute_order::execute_order_fallback_test( - // &self.testing_context, - // &auction_accounts, - // &fast_market_order_address, - // &active_auction_state, - // solver, - // config.expected_error.as_ref(), - // ) - // .await; - // if config.expected_error.is_none() { - // let order_executed_fallback_fixture = result.unwrap(); - // let order_executed_state = OrderExecutedState { - // cctp_message: order_executed_fallback_fixture.cctp_message, - // post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), - // post_message_message: Some(order_executed_fallback_fixture.post_message_message), - // }; - // TestingEngineState::OrderExecuted { - // base: current_state.base().clone(), - // initialized: current_state.initialized().unwrap().clone(), - // router_endpoints: current_state.router_endpoints().unwrap().clone(), - // fast_market_order: current_state.fast_market_order().cloned(), - // auction_state: current_state.auction_state().clone(), - // order_executed: order_executed_state, - // auction_accounts: auction_accounts.clone(), - // } - // } else { - // current_state.clone() - // } - // } + // TODO: Change to get auction accounts from current state + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let fast_market_order_address = config.fast_market_order_address.unwrap_or( + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address, + ); + let active_auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction not found"); + let result = shimful::shims_execute_order::execute_order_fallback_test( + &self.testing_context, + &auction_accounts, + &fast_market_order_address, + &active_auction_state, + solver, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let order_executed_fallback_fixture = result.unwrap(); + let order_executed_state = OrderExecutedState { + cctp_message: order_executed_fallback_fixture.cctp_message, + post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), + post_message_message: Some(order_executed_fallback_fixture.post_message_message), + }; + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } - // async fn execute_order_shimless( - // &self, - // current_state: &TestingEngineState, - // config: &ExecuteOrderInstructionConfig, - // ) -> TestingEngineState { - // let payer_signer = config - // .payer_signer - // .clone() - // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - // let auction_config_address = current_state - // .auction_config_address() - // .expect("Auction config address not found"); - // let router_endpoints = current_state - // .router_endpoints() - // .expect("Router endpoints are not created"); - // let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - // let custodian_address = current_state - // .custodian_address() - // .expect("Custodian address not found"); - // let auction_accounts = AuctionAccounts::new( - // Some( - // current_state - // .get_first_test_vaa_pair() - // .fast_transfer_vaa - // .get_vaa_pubkey(), - // ), - // solver.clone(), - // auction_config_address, - // &router_endpoints.endpoints, - // custodian_address, - // self.testing_context.get_usdc_mint_address(), - // self.testing_context.testing_state.transfer_direction, - // ); - // let result = shimless::execute_order::execute_order_shimless_test( - // &self.testing_context, - // &auction_accounts, - // current_state.auction_state(), - // &payer_signer, - // config.expected_error.as_ref(), - // ) - // .await; - // if config.expected_error.is_none() { - // let execute_order_fixture = result.unwrap(); - // let order_executed_state = OrderExecutedState { - // cctp_message: execute_order_fixture.cctp_message, - // post_message_sequence: None, - // post_message_message: None, - // }; - // TestingEngineState::OrderExecuted { - // base: current_state.base().clone(), - // initialized: current_state.initialized().unwrap().clone(), - // router_endpoints: current_state.router_endpoints().unwrap().clone(), - // fast_market_order: current_state.fast_market_order().cloned(), - // auction_state: current_state.auction_state().clone(), - // order_executed: order_executed_state, - // auction_accounts: auction_accounts.clone(), - // } - // } else { - // current_state.clone() - // } - // } + async fn execute_order_shimless( + &self, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, + ) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + let auction_config_address = current_state + .auction_config_address() + .expect("Auction config address not found"); + let router_endpoints = current_state + .router_endpoints() + .expect("Router endpoints are not created"); + let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); + let auction_accounts = AuctionAccounts::new( + Some( + current_state + .get_first_test_vaa_pair() + .fast_transfer_vaa + .get_vaa_pubkey(), + ), + solver.clone(), + auction_config_address, + &router_endpoints.endpoints, + custodian_address, + self.testing_context.get_usdc_mint_address(), + self.testing_context.testing_state.transfer_direction, + ); + let result = shimless::execute_order::execute_order_shimless_test( + &self.testing_context, + &auction_accounts, + current_state.auction_state(), + &payer_signer, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let execute_order_fixture = result.unwrap(); + let order_executed_state = OrderExecutedState { + cctp_message: execute_order_fixture.cctp_message, + post_message_sequence: None, + post_message_message: None, + }; + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } // async fn prepare_order_shim( // &self, From 0da6cb40d5af049e9e99884af1e54aef56c69893 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 27 Mar 2025 17:06:18 +0000 Subject: [PATCH 042/112] make lint --- .../src/fallback/processor/execute_order.rs | 6 +++--- .../tests/shimful/shims_execute_order.rs | 14 +++++++------- .../tests/shimless/execute_order.rs | 4 ++-- .../matching-engine/tests/testing_engine/engine.rs | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index cfe141c47..57eb9bb42 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -442,7 +442,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let fast_market_order_data = &fast_market_order_account.data.borrow()[8..]; // Deserialise fast market order. Unwrap is safe because the account is owned by the matching engine program. let fast_market_order = - bytemuck::try_from_bytes::(&fast_market_order_data[..]).unwrap(); + bytemuck::try_from_bytes::(fast_market_order_data).unwrap(); // Prepare the execute order (get the user amount, fill, and order executed event) let active_auction_info = active_auction.info.as_ref().unwrap(); @@ -639,7 +639,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { order_sender: fast_market_order.sender, redeemer: fast_market_order.redeemer, redeemer_message: fast_market_order.redeemer_message - [..fast_market_order.redeemer_message_length as usize] + [..usize::from(fast_market_order.redeemer_message_length)] .to_vec() .try_into() .unwrap(), @@ -690,7 +690,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { common::wormhole_cctp_solana::cpi::BurnAndPublishArgs { burn_source: None, destination_caller: to_router_endpoint.address, - destination_cctp_domain: destination_cctp_domain, + destination_cctp_domain, amount: user_amount, mint_recipient: to_router_endpoint.mint_recipient, wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs index b4eb54770..7b13d24e7 100644 --- a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs +++ b/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs @@ -58,15 +58,15 @@ impl ExecuteOrderFallbackAccounts { }; Self { - signer: signer.clone(), + signer: *signer, custodian: auction_accounts.custodian, - fast_market_order_address: fast_market_order_address.clone(), + fast_market_order_address: *fast_market_order_address, active_auction: active_auction_state.auction_address, active_auction_custody_token: active_auction_state.auction_custody_token_address, active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, - initial_participant: signer.clone(), + initial_participant: *signer, to_router_endpoint: auction_accounts.to_router_endpoint, remote_token_messenger, token_messenger: fixture_accounts.token_messenger, @@ -162,7 +162,7 @@ pub async fn execute_order_fallback( cctp_deposit_for_burn_token_minter: &token_minter, // 20 cctp_deposit_for_burn_local_token: &local_token, // 21 cctp_deposit_for_burn_token_messenger_minter_event_authority: - &token_messenger_minter_event_authority, // 22 + token_messenger_minter_event_authority, // 22 cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 @@ -175,14 +175,14 @@ pub async fn execute_order_fallback( }; let execute_order_ix = ExecuteOrderCctpShim { - program_id: program_id, + program_id, accounts: execute_order_ix_accounts, } .instruction(); // Considering fast forwarding blocks here for deadline to be reached let recent_blockhash = test_ctx.borrow().last_blockhash; - utils::setup::fast_forward_slots(&testing_context, 3).await; + utils::setup::fast_forward_slots(testing_context, 3).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), @@ -230,7 +230,7 @@ pub async fn execute_order_fallback_test( testing_context.testing_state.transfer_direction, ); execute_order_fallback( - &testing_context, + testing_context, &testing_context.testing_actors.owner.keypair(), &testing_context.get_matching_engine_program_id(), solver, diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/programs/matching-engine/tests/shimless/execute_order.rs index 56b9d7bcb..a3cdb21c5 100644 --- a/solana/programs/matching-engine/tests/shimless/execute_order.rs +++ b/solana/programs/matching-engine/tests/shimless/execute_order.rs @@ -57,7 +57,7 @@ pub fn create_execute_order_shimless_accounts( }; let wormhole_publish_message = WormholePublishMessage { config: wormhole_svm_definitions::solana::CORE_BRIDGE_CONFIG, - emitter_sequence: emitter_sequence, + emitter_sequence, fee_collector: wormhole_svm_definitions::solana::CORE_BRIDGE_FEE_COLLECTOR, core_bridge_program: wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, }; @@ -158,7 +158,7 @@ pub async fn execute_order_shimless_test( payer_signer: &Rc, expected_error: Option<&ExpectedError>, ) -> Option { - crate::utils::setup::fast_forward_slots(&testing_context, 3).await; + crate::utils::setup::fast_forward_slots(testing_context, 3).await; let fixture_accounts = testing_context .get_fixture_accounts() .expect("Fixture accounts not found"); diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/programs/matching-engine/tests/testing_engine/engine.rs index 09c3bde25..1a01d909f 100644 --- a/solana/programs/matching-engine/tests/testing_engine/engine.rs +++ b/solana/programs/matching-engine/tests/testing_engine/engine.rs @@ -536,21 +536,21 @@ impl TestingEngine { let auction_accounts = current_state .auction_accounts() .expect("Auction accounts not found"); - let fast_market_order_address = config.fast_market_order_address.unwrap_or( + let fast_market_order_address = config.fast_market_order_address.unwrap_or_else(|| { current_state .fast_market_order() .expect("Fast market order is not created") - .fast_market_order_address, - ); + .fast_market_order_address + }); let active_auction_state = current_state .auction_state() .get_active_auction() .expect("Active auction not found"); let result = shimful::shims_execute_order::execute_order_fallback_test( &self.testing_context, - &auction_accounts, + auction_accounts, &fast_market_order_address, - &active_auction_state, + active_auction_state, solver, config.expected_error.as_ref(), ) @@ -584,7 +584,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or(self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let auction_config_address = current_state .auction_config_address() .expect("Auction config address not found"); From 6aefcce7f364ca4784c8f07342c1200cfbc5ac33 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 28 Mar 2025 15:32:35 +0000 Subject: [PATCH 043/112] moved tests into modules --- solana/Cargo.lock | 36 ++++++++++ .../matching-engine-testing/Cargo.toml | 66 ++++++++++++++++++ .../matching-engine-testing/src/lib.rs | 14 ++++ .../matching-engine-testing}/tests/README.md | 0 .../fixtures/accounts/core_bridge/config.json | 0 .../accounts/core_bridge/fee_collector.json | 0 .../accounts/core_bridge/guardian_set_0.json | 0 .../message_transmitter_config.json | 0 .../testnet/matching_engine_custodian.json | 0 .../testnet/token_router_custodian.json | 0 .../token_router_program_data_hacked.json | 0 .../arbitrum_remote_token_messenger.json | 0 .../ethereum_remote_token_messenger.json | 0 .../misconfigured_remote_token_messenger.json | 0 .../token_messenger.json | 0 .../token_messenger_minter/token_minter.json | 0 .../usdc_custody_token.json | 0 .../usdc_local_token.json | 0 .../usdc_token_pair.json | 0 .../tests/fixtures/accounts/usdc_mint.json | 0 .../fixtures/accounts/usdc_payer_token.json | 0 .../tests/fixtures/lup.json | 0 .../mainnet_cctp_message_transmitter.so | Bin .../mainnet_cctp_token_messenger_minter.so | Bin .../tests/fixtures/mainnet_core_bridge.so | Bin .../tests/fixtures/token_router.so | Bin .../tests/fixtures/upgrade_manager.so | Bin .../tests/fixtures/usdc_mint.json | 0 .../tests/fixtures/usdc_mint_devnet.json | 0 .../fixtures/wormhole_post_message_shim.so | Bin .../fixtures/wormhole_verify_vaa_shim.so | Bin .../tests/integration_tests.rs | 0 ...bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json | 0 .../tests/shimful/README.md | 0 .../tests/shimful/fast_market_order_shim.rs | 0 .../tests/shimful/mod.rs | 0 .../tests/shimful/post_message.rs | 0 .../tests/shimful/shims_execute_order.rs | 0 .../tests/shimful/shims_make_offer.rs | 0 .../shimful/shims_prepare_order_response.rs | 0 .../tests/shimful/verify_shim.rs | 0 .../tests/shimless/README.md | 0 .../tests/shimless/execute_order.rs | 0 .../tests/shimless/initialize.rs | 0 .../tests/shimless/make_offer.rs | 0 .../tests/shimless/mod.rs | 0 .../tests/shimless/prepare_order_response.rs | 0 .../tests/shimless/settle_auction.rs | 0 .../tests/testing_engine/config.rs | 0 .../tests/testing_engine/engine.rs | 0 .../tests/testing_engine/mod.rs | 0 .../tests/testing_engine/state.rs | 0 .../tests/utils/account_fixtures.rs | 0 .../tests/utils/airdrop.rs | 0 .../tests/utils/auction.rs | 0 .../tests/utils/cctp_message.rs | 0 .../tests/utils/constants.rs | 0 .../tests/utils/mint.rs | 0 .../tests/utils/mod.rs | 0 .../tests/utils/program_fixtures.rs | 0 .../tests/utils/router.rs | 0 .../tests/utils/setup.rs | 0 .../tests/utils/token_account.rs | 0 .../tests/utils/tracing.rs | 0 .../tests/utils/vaa.rs | 0 .../processor/close_fast_market_order.rs | 16 +++-- 66 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 solana/modules/matching-engine-testing/Cargo.toml create mode 100644 solana/modules/matching-engine-testing/src/lib.rs rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/README.md (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/core_bridge/config.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/core_bridge/fee_collector.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/core_bridge/guardian_set_0.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/testnet/matching_engine_custodian.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/testnet/token_router_custodian.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/token_messenger.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/token_minter.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/usdc_mint.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/accounts/usdc_payer_token.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/lup.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/mainnet_cctp_message_transmitter.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/mainnet_cctp_token_messenger_minter.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/mainnet_core_bridge.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/token_router.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/upgrade_manager.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/usdc_mint.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/usdc_mint_devnet.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/wormhole_post_message_shim.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/fixtures/wormhole_verify_vaa_shim.so (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/integration_tests.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/README.md (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/fast_market_order_shim.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/mod.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/post_message.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/shims_execute_order.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/shims_make_offer.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/shims_prepare_order_response.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimful/verify_shim.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/README.md (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/execute_order.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/initialize.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/make_offer.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/mod.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/prepare_order_response.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/shimless/settle_auction.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/testing_engine/config.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/testing_engine/engine.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/testing_engine/mod.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/testing_engine/state.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/account_fixtures.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/airdrop.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/auction.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/cctp_message.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/constants.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/mint.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/mod.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/program_fixtures.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/router.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/setup.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/token_account.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/tracing.rs (100%) rename solana/{programs/matching-engine => modules/matching-engine-testing}/tests/utils/vaa.rs (100%) diff --git a/solana/Cargo.lock b/solana/Cargo.lock index e5a70aaf3..839efc20e 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -2574,6 +2574,42 @@ dependencies = [ "wormhole-svm-shim", ] +[[package]] +name = "matching-engine-testing" +version = "0.0.0" +dependencies = [ + "anchor-lang", + "anchor-spl", + "anyhow", + "base64 0.22.1", + "bincode", + "bs58 0.5.0", + "bytemuck", + "cfg-if", + "hex", + "hex-literal", + "lazy_static", + "liquidity-layer-common-solana", + "matching-engine", + "num-traits", + "once_cell", + "ruint", + "secp256k1", + "serde", + "serde_json", + "solana-cli-output", + "solana-program", + "solana-program-test", + "solana-sdk", + "tracing", + "tracing-log", + "tracing-subscriber", + "wormhole-io", + "wormhole-solana-utils", + "wormhole-svm-definitions", + "wormhole-svm-shim", +] + [[package]] name = "memchr" version = "2.7.1" diff --git a/solana/modules/matching-engine-testing/Cargo.toml b/solana/modules/matching-engine-testing/Cargo.toml new file mode 100644 index 000000000..62fb2edf8 --- /dev/null +++ b/solana/modules/matching-engine-testing/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "matching-engine-testing" +edition.workspace = true +version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["no-idl"] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +mainnet = ["matching-engine/mainnet", "common/mainnet"] +testnet = ["matching-engine/testnet", "common/testnet"] +localnet = ["matching-engine/localnet", "common/localnet"] +integration-test = ["localnet"] +idl-build = [ + "localnet", + "common/idl-build", + "anchor-lang/idl-build", + "anchor-spl/idl-build" +] + +[dev-dependencies] +matching-engine.workspace = true +hex-literal.workspace = true +wormhole-io.workspace = true +common.workspace = true +wormhole-solana-utils.workspace = true + +anchor-lang = { workspace = true, features = ["event-cpi", "init-if-needed"] } +anchor-spl.workspace = true +solana-program.workspace = true + +hex.workspace = true +bytemuck.workspace = true +ruint.workspace = true +cfg-if.workspace = true +wormhole-svm-definitions.workspace = true +wormhole-svm-shim.workspace = true + +solana-program-test = "1.18.15" +solana-sdk = "1.18.15" +serde_json = "1.0.138" +bincode = "1.3.3" +solana-cli-output = "1.18.15" +base64 = "0.22.1" +lazy_static = "1.4.0" +bs58 = "0.5.0" +serde = { version = "1.0.212", features = ["derive"] } +secp256k1 = {version = "0.30.0", features = ["rand", "hashes", "std", "global-context", "recovery"] } +num-traits = "0.2.16" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-log = "0.2.0" +once_cell = "1.8" +anyhow = "1.0.97" + +[lints] +workspace = true + diff --git a/solana/modules/matching-engine-testing/src/lib.rs b/solana/modules/matching-engine-testing/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/solana/modules/matching-engine-testing/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/solana/programs/matching-engine/tests/README.md b/solana/modules/matching-engine-testing/tests/README.md similarity index 100% rename from solana/programs/matching-engine/tests/README.md rename to solana/modules/matching-engine-testing/tests/README.md diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/config.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/config.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/config.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/fee_collector.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/fee_collector.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/fee_collector.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/guardian_set_0.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/core_bridge/guardian_set_0.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/core_bridge/guardian_set_0.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/message_transmitter/message_transmitter_config.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/matching_engine_custodian.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/testnet/matching_engine_custodian.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/matching_engine_custodian.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/token_router_custodian.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_custodian.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/token_router_custodian.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/testnet/token_router_program_data_hacked.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/ethereum_remote_token_messenger.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/misconfigured_remote_token_messenger.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/token_messenger.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_messenger.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/token_messenger.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/token_minter.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/token_minter.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/token_minter.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_custody_token.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_local_token.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/token_messenger_minter/usdc_token_pair.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/usdc_mint.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/usdc_mint.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/usdc_mint.json diff --git a/solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json b/solana/modules/matching-engine-testing/tests/fixtures/accounts/usdc_payer_token.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/accounts/usdc_payer_token.json rename to solana/modules/matching-engine-testing/tests/fixtures/accounts/usdc_payer_token.json diff --git a/solana/programs/matching-engine/tests/fixtures/lup.json b/solana/modules/matching-engine-testing/tests/fixtures/lup.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/lup.json rename to solana/modules/matching-engine-testing/tests/fixtures/lup.json diff --git a/solana/programs/matching-engine/tests/fixtures/mainnet_cctp_message_transmitter.so b/solana/modules/matching-engine-testing/tests/fixtures/mainnet_cctp_message_transmitter.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/mainnet_cctp_message_transmitter.so rename to solana/modules/matching-engine-testing/tests/fixtures/mainnet_cctp_message_transmitter.so diff --git a/solana/programs/matching-engine/tests/fixtures/mainnet_cctp_token_messenger_minter.so b/solana/modules/matching-engine-testing/tests/fixtures/mainnet_cctp_token_messenger_minter.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/mainnet_cctp_token_messenger_minter.so rename to solana/modules/matching-engine-testing/tests/fixtures/mainnet_cctp_token_messenger_minter.so diff --git a/solana/programs/matching-engine/tests/fixtures/mainnet_core_bridge.so b/solana/modules/matching-engine-testing/tests/fixtures/mainnet_core_bridge.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/mainnet_core_bridge.so rename to solana/modules/matching-engine-testing/tests/fixtures/mainnet_core_bridge.so diff --git a/solana/programs/matching-engine/tests/fixtures/token_router.so b/solana/modules/matching-engine-testing/tests/fixtures/token_router.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/token_router.so rename to solana/modules/matching-engine-testing/tests/fixtures/token_router.so diff --git a/solana/programs/matching-engine/tests/fixtures/upgrade_manager.so b/solana/modules/matching-engine-testing/tests/fixtures/upgrade_manager.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/upgrade_manager.so rename to solana/modules/matching-engine-testing/tests/fixtures/upgrade_manager.so diff --git a/solana/programs/matching-engine/tests/fixtures/usdc_mint.json b/solana/modules/matching-engine-testing/tests/fixtures/usdc_mint.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/usdc_mint.json rename to solana/modules/matching-engine-testing/tests/fixtures/usdc_mint.json diff --git a/solana/programs/matching-engine/tests/fixtures/usdc_mint_devnet.json b/solana/modules/matching-engine-testing/tests/fixtures/usdc_mint_devnet.json similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/usdc_mint_devnet.json rename to solana/modules/matching-engine-testing/tests/fixtures/usdc_mint_devnet.json diff --git a/solana/programs/matching-engine/tests/fixtures/wormhole_post_message_shim.so b/solana/modules/matching-engine-testing/tests/fixtures/wormhole_post_message_shim.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/wormhole_post_message_shim.so rename to solana/modules/matching-engine-testing/tests/fixtures/wormhole_post_message_shim.so diff --git a/solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so b/solana/modules/matching-engine-testing/tests/fixtures/wormhole_verify_vaa_shim.so similarity index 100% rename from solana/programs/matching-engine/tests/fixtures/wormhole_verify_vaa_shim.so rename to solana/modules/matching-engine-testing/tests/fixtures/wormhole_verify_vaa_shim.so diff --git a/solana/programs/matching-engine/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs similarity index 100% rename from solana/programs/matching-engine/tests/integration_tests.rs rename to solana/modules/matching-engine-testing/tests/integration_tests.rs diff --git a/solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json b/solana/modules/matching-engine-testing/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json similarity index 100% rename from solana/programs/matching-engine/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json rename to solana/modules/matching-engine-testing/tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json diff --git a/solana/programs/matching-engine/tests/shimful/README.md b/solana/modules/matching-engine-testing/tests/shimful/README.md similarity index 100% rename from solana/programs/matching-engine/tests/shimful/README.md rename to solana/modules/matching-engine-testing/tests/shimful/README.md diff --git a/solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/fast_market_order_shim.rs rename to solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs diff --git a/solana/programs/matching-engine/tests/shimful/mod.rs b/solana/modules/matching-engine-testing/tests/shimful/mod.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/mod.rs rename to solana/modules/matching-engine-testing/tests/shimful/mod.rs diff --git a/solana/programs/matching-engine/tests/shimful/post_message.rs b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/post_message.rs rename to solana/modules/matching-engine-testing/tests/shimful/post_message.rs diff --git a/solana/programs/matching-engine/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/shims_execute_order.rs rename to solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs diff --git a/solana/programs/matching-engine/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/shims_make_offer.rs rename to solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs diff --git a/solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/shims_prepare_order_response.rs rename to solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs diff --git a/solana/programs/matching-engine/tests/shimful/verify_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimful/verify_shim.rs rename to solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs diff --git a/solana/programs/matching-engine/tests/shimless/README.md b/solana/modules/matching-engine-testing/tests/shimless/README.md similarity index 100% rename from solana/programs/matching-engine/tests/shimless/README.md rename to solana/modules/matching-engine-testing/tests/shimless/README.md diff --git a/solana/programs/matching-engine/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/execute_order.rs rename to solana/modules/matching-engine-testing/tests/shimless/execute_order.rs diff --git a/solana/programs/matching-engine/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/initialize.rs rename to solana/modules/matching-engine-testing/tests/shimless/initialize.rs diff --git a/solana/programs/matching-engine/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/make_offer.rs rename to solana/modules/matching-engine-testing/tests/shimless/make_offer.rs diff --git a/solana/programs/matching-engine/tests/shimless/mod.rs b/solana/modules/matching-engine-testing/tests/shimless/mod.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/mod.rs rename to solana/modules/matching-engine-testing/tests/shimless/mod.rs diff --git a/solana/programs/matching-engine/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/prepare_order_response.rs rename to solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs diff --git a/solana/programs/matching-engine/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs similarity index 100% rename from solana/programs/matching-engine/tests/shimless/settle_auction.rs rename to solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs diff --git a/solana/programs/matching-engine/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs similarity index 100% rename from solana/programs/matching-engine/tests/testing_engine/config.rs rename to solana/modules/matching-engine-testing/tests/testing_engine/config.rs diff --git a/solana/programs/matching-engine/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs similarity index 100% rename from solana/programs/matching-engine/tests/testing_engine/engine.rs rename to solana/modules/matching-engine-testing/tests/testing_engine/engine.rs diff --git a/solana/programs/matching-engine/tests/testing_engine/mod.rs b/solana/modules/matching-engine-testing/tests/testing_engine/mod.rs similarity index 100% rename from solana/programs/matching-engine/tests/testing_engine/mod.rs rename to solana/modules/matching-engine-testing/tests/testing_engine/mod.rs diff --git a/solana/programs/matching-engine/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs similarity index 100% rename from solana/programs/matching-engine/tests/testing_engine/state.rs rename to solana/modules/matching-engine-testing/tests/testing_engine/state.rs diff --git a/solana/programs/matching-engine/tests/utils/account_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/account_fixtures.rs rename to solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs diff --git a/solana/programs/matching-engine/tests/utils/airdrop.rs b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/airdrop.rs rename to solana/modules/matching-engine-testing/tests/utils/airdrop.rs diff --git a/solana/programs/matching-engine/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/auction.rs rename to solana/modules/matching-engine-testing/tests/utils/auction.rs diff --git a/solana/programs/matching-engine/tests/utils/cctp_message.rs b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/cctp_message.rs rename to solana/modules/matching-engine-testing/tests/utils/cctp_message.rs diff --git a/solana/programs/matching-engine/tests/utils/constants.rs b/solana/modules/matching-engine-testing/tests/utils/constants.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/constants.rs rename to solana/modules/matching-engine-testing/tests/utils/constants.rs diff --git a/solana/programs/matching-engine/tests/utils/mint.rs b/solana/modules/matching-engine-testing/tests/utils/mint.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/mint.rs rename to solana/modules/matching-engine-testing/tests/utils/mint.rs diff --git a/solana/programs/matching-engine/tests/utils/mod.rs b/solana/modules/matching-engine-testing/tests/utils/mod.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/mod.rs rename to solana/modules/matching-engine-testing/tests/utils/mod.rs diff --git a/solana/programs/matching-engine/tests/utils/program_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/program_fixtures.rs rename to solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs diff --git a/solana/programs/matching-engine/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/router.rs rename to solana/modules/matching-engine-testing/tests/utils/router.rs diff --git a/solana/programs/matching-engine/tests/utils/setup.rs b/solana/modules/matching-engine-testing/tests/utils/setup.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/setup.rs rename to solana/modules/matching-engine-testing/tests/utils/setup.rs diff --git a/solana/programs/matching-engine/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/token_account.rs rename to solana/modules/matching-engine-testing/tests/utils/token_account.rs diff --git a/solana/programs/matching-engine/tests/utils/tracing.rs b/solana/modules/matching-engine-testing/tests/utils/tracing.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/tracing.rs rename to solana/modules/matching-engine-testing/tests/utils/tracing.rs diff --git a/solana/programs/matching-engine/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs similarity index 100% rename from solana/programs/matching-engine/tests/utils/vaa.rs rename to solana/modules/matching-engine-testing/tests/utils/vaa.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 220b80e42..285bbe9b6 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -71,11 +71,19 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { }); } - // Transfer the lamports from the fast market order to the close account refund recipient + // First, get the current lamports value + let current_recipient_lamports = **close_account_refund_recipient.lamports.borrow(); + + // Then, get the fast market order lamports let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut(); - **close_account_refund_recipient.lamports.borrow_mut() = - (**close_account_refund_recipient.lamports.borrow()) - .saturating_add(**fast_market_order_lamports); + + // Calculate the new amount + let new_amount = current_recipient_lamports.saturating_add(**fast_market_order_lamports); + + // Now update the recipient's lamports + **close_account_refund_recipient.lamports.borrow_mut() = new_amount; + + // Zero out the fast market order lamports **fast_market_order_lamports = 0; Ok(()) From 08d3fd12828717d75976cd76a4e1a6c979253452 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 28 Mar 2025 16:50:46 +0000 Subject: [PATCH 044/112] clippy --- .../matching-engine-testing/src/lib.rs | 2 +- .../tests/integration_tests.rs | 395 +++++++++--------- .../tests/shimful/mod.rs | 2 +- .../shimful/shims_prepare_order_response.rs | 25 +- .../tests/shimless/mod.rs | 4 +- .../tests/shimless/prepare_order_response.rs | 46 +- .../tests/shimless/settle_auction.rs | 10 +- .../tests/testing_engine/config.rs | 3 +- .../tests/testing_engine/engine.rs | 310 +++++++------- .../tests/utils/cctp_message.rs | 26 +- .../tests/utils/mod.rs | 2 +- .../tests/utils/setup.rs | 2 +- .../src/fallback/processor/mod.rs | 2 +- .../processor/prepare_order_response.rs | 14 +- .../fallback/processor/process_instruction.rs | 44 +- solana/programs/matching-engine/src/lib.rs | 2 +- 16 files changed, 453 insertions(+), 436 deletions(-) diff --git a/solana/modules/matching-engine-testing/src/lib.rs b/solana/modules/matching-engine-testing/src/lib.rs index 7d12d9af8..91cfe393b 100644 --- a/solana/modules/matching-engine-testing/src/lib.rs +++ b/solana/modules/matching-engine-testing/src/lib.rs @@ -1,5 +1,5 @@ pub fn add(left: usize, right: usize) -> usize { - left + right + left.saturating_add(right) } #[cfg(test)] diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs index daf45eab5..ec7ae789c 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/integration_tests.rs @@ -5,6 +5,7 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; +use matching_engine::error::MatchingEngineError; use matching_engine::ID as PROGRAM_ID; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -412,200 +413,200 @@ pub async fn test_execute_order_shimless() { ]; testing_engine.execute(instruction_triggers).await; } -// pub async fn test_execute_order_fallback_blocks_shimless() { -// let transfer_direction = TransferDirection::FromArbitrumToEthereum; -// let vaa_args = VaaArgs { -// post_vaa: true, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { -// expected_error: Some(ExpectedError { -// instruction_index: 0, -// error_code: MatchingEngineError::AccountAlreadyInitialized.into(), -// error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), -// }), -// ..ExecuteOrderInstructionConfig::default() -// }), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } - -// // From ethereum to arbitrum -// #[tokio::test] -// pub async fn test_prepare_order_shim_fallback() { -// let transfer_direction = TransferDirection::FromEthereumToArbitrum; -// let vaa_args = VaaArgs { -// post_vaa: false, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } - -// // Prepare order response from ethereum to arbitrum (shimless) -// #[tokio::test] -// pub async fn test_prepare_order_shimless() { -// let transfer_direction = TransferDirection::FromEthereumToArbitrum; -// let vaa_args = VaaArgs { -// post_vaa: true, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } - -// #[tokio::test] -// pub async fn test_prepare_order_response_shimful_blocks_shimless() { -// let transfer_direction = TransferDirection::FromEthereumToArbitrum; -// let vaa_args = VaaArgs { -// post_vaa: true, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), -// //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them -// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { -// // expected_log_message: Some("Already prepared".to_string()), -// ..PrepareOrderInstructionConfig::default() -// }), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } - -// #[tokio::test] -// pub async fn test_prepare_order_response_shimless_blocks_shimful() { -// let transfer_direction = TransferDirection::FromEthereumToArbitrum; -// let vaa_args = VaaArgs { -// post_vaa: true, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), -// // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above -// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { -// expected_error: Some(ExpectedError { -// instruction_index: 0, -// error_code: 0, -// error_string: TransactionError::AccountInUse.to_string(), -// }), -// ..PrepareOrderInstructionConfig::default() -// }), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } - -// #[tokio::test] -// pub async fn test_settle_auction_complete() { -// let transfer_direction = TransferDirection::FromEthereumToArbitrum; -// let vaa_args = VaaArgs { -// post_vaa: false, -// ..VaaArgs::default() -// }; -// let testing_context = setup_environment( -// ShimMode::VerifyAndPostSignature, -// transfer_direction, -// Some(vaa_args), -// ) -// .await; - -// let testing_engine = TestingEngine::new(testing_context).await; -// let instruction_triggers = vec![ -// InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), -// InstructionTrigger::CreateCctpRouterEndpoints( -// CreateCctpRouterEndpointsInstructionConfig::default(), -// ), -// InstructionTrigger::InitializeFastMarketOrderShim( -// InitializeFastMarketOrderShimInstructionConfig::default(), -// ), -// InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), -// InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), -// InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), -// InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), -// ]; -// testing_engine.execute(instruction_triggers).await; -// } +pub async fn test_execute_order_fallback_blocks_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: MatchingEngineError::AccountAlreadyInitialized.into(), + error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), + }), + ..ExecuteOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +// From ethereum to arbitrum +#[tokio::test] +pub async fn test_prepare_order_shim_fallback() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} + +// Prepare order response from ethereum to arbitrum (shimless) +#[tokio::test] +pub async fn test_prepare_order_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_prepare_order_response_shimful_blocks_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { + // expected_log_message: Some("Already prepared".to_string()), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_prepare_order_response_shimless_blocks_shimful() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine.execute(instruction_triggers).await; +} + +#[tokio::test] +pub async fn test_settle_auction_complete() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let testing_context = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine.execute(instruction_triggers).await; +} diff --git a/solana/modules/matching-engine-testing/tests/shimful/mod.rs b/solana/modules/matching-engine-testing/tests/shimful/mod.rs index 6553af252..dcbf6c9a0 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/mod.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/mod.rs @@ -3,5 +3,5 @@ pub mod fast_market_order_shim; pub mod post_message; pub mod shims_execute_order; pub mod shims_make_offer; -// pub mod shims_prepare_order_response; +pub mod shims_prepare_order_response; pub mod verify_shim; diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index 22fe8b19c..d8e07c2a3 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -51,6 +51,7 @@ pub struct PrepareOrderResponseShimAccountsFixture { } impl PrepareOrderResponseShimAccountsFixture { + #[allow(clippy::too_many_arguments)] pub fn new( signer: &Pubkey, fixture_accounts: &FixtureAccounts, @@ -155,7 +156,7 @@ impl PrepareOrderResponseShimDataFixture { finalized_vaa_message_base_fee: deposit_base_fee, vaa_payload: deposit_vaa_data.payload.to_vec(), deposit_payload: deposit.payload.to_vec(), - fast_market_order: fast_market_order.clone(), + fast_market_order: *fast_market_order, guardian_set_bump, } } @@ -270,6 +271,7 @@ pub async fn prepare_order_response_cctp_shim( } } +#[allow(clippy::too_many_arguments)] pub async fn prepare_order_response_test( testing_context: &TestingContext, payer_signer: &Rc, @@ -293,18 +295,19 @@ pub async fn prepare_order_response_test( let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = super::verify_shim::create_guardian_signatures( - test_ctx, + testing_context, payer_signer, deposit_vaa_data, core_bridge_program_id, None, ) - .await; + .await + .unwrap(); let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { utils::router::get_remote_token_messenger( - test_ctx, + testing_context, fixture_accounts.ethereum_remote_token_messenger, ) .await @@ -338,12 +341,12 @@ pub async fn prepare_order_response_test( .verify_cctp_message(&fast_market_order_state) .unwrap(); - let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); + let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(deposit); let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new( cctp_token_burn_message.encoded_cctp_burn_message, cctp_token_burn_message.cctp_attestation, - &deposit_vaa_data, - &deposit, + deposit_vaa_data, + deposit, deposit_base_fee, &fast_market_order_state, guardian_set_bump, @@ -358,9 +361,9 @@ pub async fn prepare_order_response_test( &fixture_accounts, &custodian_address, &fast_market_order_address, - &from_endpoint_address, - &to_endpoint_address, - &usdc_mint_address, + from_endpoint_address, + to_endpoint_address, + usdc_mint_address, &cctp_message_decoded, &guardian_set_pubkey, &guardian_signatures_pubkey, @@ -371,7 +374,7 @@ pub async fn prepare_order_response_test( payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, - &matching_engine_program_id, + matching_engine_program_id, expected_error, ) .await diff --git a/solana/modules/matching-engine-testing/tests/shimless/mod.rs b/solana/modules/matching-engine-testing/tests/shimless/mod.rs index 5e02d8d2b..eb45b45ee 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/mod.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/mod.rs @@ -3,5 +3,5 @@ pub mod execute_order; pub mod initialize; pub mod make_offer; -// pub mod prepare_order_response; -// pub mod settle_auction; +pub mod prepare_order_response; +pub mod settle_auction; diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index b5f7032df..00b78d9b6 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -1,3 +1,4 @@ +use crate::testing_engine::config::ExpectedLog; use crate::testing_engine::state::TestingEngineState; use crate::utils; use crate::utils::cctp_message::UsedNonces; @@ -27,6 +28,8 @@ pub struct PrepareOrderResponseFixture { pub prepared_order_response: Pubkey, pub prepared_custody_token: Pubkey, } + +#[allow(clippy::too_many_arguments)] pub async fn prepare_order_response( testing_context: &TestingContext, payer_signer: &Rc, @@ -35,7 +38,7 @@ pub async fn prepare_order_response( from_endpoint_address: &Pubkey, base_fee_token_address: &Pubkey, expected_error: Option<&ExpectedError>, - expected_log_message: Option<&String>, + expected_log_message: Option<&Vec>, ) -> Option { let test_ctx = &testing_context.test_context; let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); @@ -49,7 +52,7 @@ pub async fn prepare_order_response( let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { utils::router::get_remote_token_messenger( - test_ctx, + testing_context, fixture_accounts.ethereum_remote_token_messenger, ) .await @@ -95,10 +98,10 @@ pub async fn prepare_order_response( fast_vaa: fast_transfer_liquidity_layer_vaa, path: LiveRouterPath { to_endpoint: LiveRouterEndpoint { - endpoint: to_endpoint_address.clone(), + endpoint: *to_endpoint_address, }, from_endpoint: LiveRouterEndpoint { - endpoint: from_endpoint_address.clone(), + endpoint: *from_endpoint_address, }, }, }; @@ -120,7 +123,7 @@ pub async fn prepare_order_response( Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); let usdc = Usdc { - mint: usdc_mint_address.clone(), + mint: *usdc_mint_address, }; let (used_nonces_pda, _used_nonces_bump) = UsedNonces::address(source_remote_token_messenger.domain, cctp_nonce); @@ -136,7 +139,7 @@ pub async fn prepare_order_response( Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; let cctp_mint_recipient = CctpMintRecipientMut { - mint_recipient: cctp_mint_recipient.clone(), + mint_recipient: *cctp_mint_recipient, }; let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; @@ -151,30 +154,30 @@ pub async fn prepare_order_response( }; let cctp = CctpReceiveMessage { mint_recipient: cctp_mint_recipient, - message_transmitter_authority: cctp_message_transmitter_authority.clone(), - message_transmitter_config: message_transmitter_config_pubkey.clone(), + message_transmitter_authority: cctp_message_transmitter_authority, + message_transmitter_config: message_transmitter_config_pubkey, used_nonces: used_nonces_pda, message_transmitter_event_authority: cctp_message_transmitter_event_authority, - token_messenger: fixture_accounts.token_messenger.clone(), + token_messenger: fixture_accounts.token_messenger, remote_token_messenger: cctp_remote_token_messenger, - token_minter: fixture_accounts.token_minter.clone(), - local_token: fixture_accounts.usdc_local_token.clone(), - token_pair: fixture_accounts.usdc_token_pair.clone(), - token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token.clone(), - token_messenger_minter_event_authority: token_messenger_minter_event_authority, + token_minter: fixture_accounts.token_minter, + local_token: fixture_accounts.usdc_local_token, + token_pair: fixture_accounts.usdc_token_pair, + token_messenger_minter_custody_token: fixture_accounts.usdc_custody_token, + token_messenger_minter_event_authority, token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, }; let prepared_order_response_accounts = PrepareOrderResponseCctpAccounts { payer: payer_signer.pubkey(), custodian: checked_custodian, - fast_order_path: fast_order_path, - finalized_vaa: finalized_vaa, + fast_order_path, + finalized_vaa, prepared_order_response: prepared_order_response_pda, prepared_custody_token: prepared_custody_token_pda, base_fee_token: *base_fee_token_address, - usdc: usdc, - cctp: cctp, + usdc, + cctp, token_program: spl_token::ID, system_program: system_program::ID, }; @@ -188,7 +191,7 @@ pub async fn prepare_order_response( .data(); let instruction = Instruction { - program_id: matching_engine_program_id.clone(), + program_id: *matching_engine_program_id, accounts: prepared_order_response_accounts.to_account_metas(None), data: prepare_order_response_ix_data, }; @@ -209,8 +212,9 @@ pub async fn prepare_order_response( "Expected error is not allowed when expected log message is provided" ); testing_context - .execute_and_verify_logs(transaction, expected_log_message) - .await; + .simulate_and_verify_logs(transaction, expected_log_message) + .await + .unwrap(); } else { testing_context .execute_and_verify_transaction(transaction, expected_error) diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index f6d561cb2..61dbc92d1 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -27,19 +27,19 @@ pub async fn settle_auction_complete( let active_auction = auction_state .get_active_auction() .expect("Failed to get active auction"); - let base_fee_token = usdc_mint_address.clone(); + let base_fee_token = *usdc_mint_address; let event_seeds = EVENT_AUTHORITY_SEED; let event_authority = Pubkey::find_program_address(&[event_seeds], matching_engine_program_id).0; let settle_auction_accounts = SettleAuctionCompleteCpiAccounts { beneficiary: payer_signer.pubkey(), - base_fee_token: base_fee_token, - prepared_order_response: prepare_order_response_address.clone(), - prepared_custody_token: prepared_custody_token.clone(), + base_fee_token, + prepared_order_response: *prepare_order_response_address, + prepared_custody_token: *prepared_custody_token, auction: active_auction.auction_address, best_offer_token: active_auction.best_offer.offer_token, token_program: spl_token::ID, - event_authority: event_authority, + event_authority, program: *matching_engine_program_id, }; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 603c88aa6..1cc6e8154 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -13,6 +13,7 @@ pub struct ExpectedError { pub error_string: String, } +#[derive(Clone)] pub struct ExpectedLog { pub log_message: String, pub count: usize, @@ -53,7 +54,7 @@ pub struct PrepareOrderInstructionConfig { pub solver_index: usize, pub payer_signer: Option>, pub expected_error: Option, - pub expected_log_message: Option, + pub expected_log_messages: Option>, } #[derive(Clone, Default)] diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 1a01d909f..d6849bad7 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -7,6 +7,7 @@ use crate::shimful::fast_market_order_shim::{ }; use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; +use crate::utils::auction::AuctionState; use crate::utils::vaa::TestVaaPairs; use crate::utils::{ auction::{ @@ -29,9 +30,9 @@ pub enum InstructionTrigger { ImproveOfferShimless(ImproveOfferInstructionConfig), ExecuteOrderShimless(ExecuteOrderInstructionConfig), ExecuteOrderShim(ExecuteOrderInstructionConfig), - // PrepareOrderShimless(PrepareOrderInstructionConfig), - // PrepareOrderShim(PrepareOrderInstructionConfig), - // SettleAuction(SettleAuctionInstructionConfig), + PrepareOrderShimless(PrepareOrderInstructionConfig), + PrepareOrderShim(PrepareOrderInstructionConfig), + SettleAuction(SettleAuctionInstructionConfig), CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } @@ -124,15 +125,16 @@ impl TestingEngine { } InstructionTrigger::ExecuteOrderShimless(config) => { self.execute_order_shimless(current_state, config).await - } // InstructionTrigger::PrepareOrderShim(config) => { - // self.prepare_order_shim(current_state, config).await - // } - // InstructionTrigger::PrepareOrderShimless(config) => { - // self.prepare_order_shimless(current_state, config).await - // } - // InstructionTrigger::SettleAuction(config) => { - // self.settle_auction(current_state, config).await - // } + } + InstructionTrigger::PrepareOrderShim(config) => { + self.prepare_order_shim(current_state, config).await + } + InstructionTrigger::PrepareOrderShimless(config) => { + self.prepare_order_shimless(current_state, config).await + } + InstructionTrigger::SettleAuction(config) => { + self.settle_auction(current_state, config).await + } } } @@ -638,146 +640,146 @@ impl TestingEngine { } } - // async fn prepare_order_shim( - // &self, - // current_state: &TestingEngineState, - // config: &PrepareOrderInstructionConfig, - // ) -> TestingEngineState { - // let auction_accounts = current_state - // .auction_accounts() - // .expect("Auction accounts not found"); - - // let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); - // let deposit_vaa_data = deposit_vaa.get_vaa_data(); - // let deposit = deposit_vaa - // .payload_deserialized - // .clone() - // .unwrap() - // .get_deposit() - // .unwrap(); - - // let payer_signer = config - // .payer_signer - // .clone() - // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - - // let result = shimful::shims_prepare_order_response::prepare_order_response_test( - // &self.testing_context, - // &payer_signer, - // &deposit_vaa_data, - // current_state, - // &auction_accounts.to_router_endpoint, - // &auction_accounts.from_router_endpoint, - // &deposit, - // config.expected_error.as_ref(), - // ) - // .await; - // if config.expected_error.is_none() { - // let prepare_order_response_fixture = result.unwrap(); - // let order_prepared_state = OrderPreparedState { - // prepared_order_address: prepare_order_response_fixture.prepared_order_response, - // prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - // }; - // TestingEngineState::OrderPrepared { - // base: current_state.base().clone(), - // initialized: current_state.initialized().unwrap().clone(), - // router_endpoints: current_state.router_endpoints().unwrap().clone(), - // fast_market_order: current_state.fast_market_order().cloned(), - // auction_state: current_state.auction_state().clone(), - // order_prepared: order_prepared_state, - // auction_accounts: auction_accounts.clone(), - // } - // } else { - // current_state.clone() - // } - // } - - // async fn prepare_order_shimless( - // &self, - // current_state: &TestingEngineState, - // config: &PrepareOrderInstructionConfig, - // ) -> TestingEngineState { - // let payer_signer = config - // .payer_signer - // .clone() - // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - // let auction_accounts = current_state - // .auction_accounts() - // .expect("Auction accounts not found"); - // let solver_token_account = self - // .testing_context - // .testing_actors - // .solvers - // .get(config.solver_index) - // .expect("Solver not found at index") - // .token_account_address() - // .expect("Token account does not exist for solver at index"); - // let result = shimless::prepare_order_response::prepare_order_response( - // &self.testing_context, - // &payer_signer, - // current_state, - // &auction_accounts.to_router_endpoint, - // &auction_accounts.from_router_endpoint, - // &solver_token_account, - // config.expected_error.as_ref(), - // config.expected_log_message.as_ref(), - // ) - // .await; - // if config.expected_error.is_none() { - // let prepare_order_response_fixture = result.unwrap(); - // let order_prepared_state = OrderPreparedState { - // prepared_order_address: prepare_order_response_fixture.prepared_order_response, - // prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - // }; - // TestingEngineState::OrderPrepared { - // base: current_state.base().clone(), - // initialized: current_state.initialized().unwrap().clone(), - // router_endpoints: current_state.router_endpoints().unwrap().clone(), - // fast_market_order: current_state.fast_market_order().cloned(), - // auction_state: current_state.auction_state().clone(), - // order_prepared: order_prepared_state, - // auction_accounts: auction_accounts.clone(), - // } - // } else { - // current_state.clone() - // } - // } - - // async fn settle_auction( - // &self, - // current_state: &TestingEngineState, - // config: &SettleAuctionInstructionConfig, - // ) -> TestingEngineState { - // let payer_signer = config - // .payer_signer - // .clone() - // .unwrap_or(self.testing_context.testing_actors.owner.keypair()); - // let order_prepared_state = current_state - // .order_prepared() - // .expect("Order prepared not found"); - // let prepared_custody_token = order_prepared_state.prepared_custody_token; - // let prepared_order_response = order_prepared_state.prepared_order_address; - // let auction_state = shimless::settle_auction::settle_auction_complete( - // &self.testing_context, - // &payer_signer, - // current_state.auction_state(), - // &prepared_order_response, - // &prepared_custody_token, - // &self.testing_context.get_matching_engine_program_id(), - // config.expected_error.as_ref(), - // ) - // .await; - // match auction_state { - // AuctionState::Settled => TestingEngineState::AuctionSettled { - // base: current_state.base().clone(), - // initialized: current_state.initialized().unwrap().clone(), - // router_endpoints: current_state.router_endpoints().unwrap().clone(), - // auction_state: current_state.auction_state().clone(), - // fast_market_order: current_state.fast_market_order().cloned(), - // order_prepared: order_prepared_state.clone(), - // auction_accounts: current_state.auction_accounts().cloned(), - // }, - // _ => current_state.clone(), - // } - // } + async fn prepare_order_shim( + &self, + current_state: &TestingEngineState, + config: &PrepareOrderInstructionConfig, + ) -> TestingEngineState { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + + let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); + let deposit_vaa_data = deposit_vaa.get_vaa_data(); + let deposit = deposit_vaa + .payload_deserialized + .clone() + .unwrap() + .get_deposit() + .unwrap(); + + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + + let result = shimful::shims_prepare_order_response::prepare_order_response_test( + &self.testing_context, + &payer_signer, + deposit_vaa_data, + current_state, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, + &deposit, + config.expected_error.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let prepare_order_response_fixture = result.unwrap(); + let order_prepared_state = OrderPreparedState { + prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + }; + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } + + async fn prepare_order_shimless( + &self, + current_state: &TestingEngineState, + config: &PrepareOrderInstructionConfig, + ) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let solver_token_account = self + .testing_context + .testing_actors + .solvers + .get(config.solver_index) + .expect("Solver not found at index") + .token_account_address() + .expect("Token account does not exist for solver at index"); + let result = shimless::prepare_order_response::prepare_order_response( + &self.testing_context, + &payer_signer, + current_state, + &auction_accounts.to_router_endpoint, + &auction_accounts.from_router_endpoint, + &solver_token_account, + config.expected_error.as_ref(), + config.expected_log_messages.as_ref(), + ) + .await; + if config.expected_error.is_none() { + let prepare_order_response_fixture = result.unwrap(); + let order_prepared_state = OrderPreparedState { + prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + }; + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } + } + + async fn settle_auction( + &self, + current_state: &TestingEngineState, + config: &SettleAuctionInstructionConfig, + ) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + let order_prepared_state = current_state + .order_prepared() + .expect("Order prepared not found"); + let prepared_custody_token = order_prepared_state.prepared_custody_token; + let prepared_order_response = order_prepared_state.prepared_order_address; + let auction_state = shimless::settle_auction::settle_auction_complete( + &self.testing_context, + &payer_signer, + current_state.auction_state(), + &prepared_order_response, + &prepared_custody_token, + &self.testing_context.get_matching_engine_program_id(), + config.expected_error.as_ref(), + ) + .await; + match auction_state { + AuctionState::Settled => TestingEngineState::AuctionSettled { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + auction_state: current_state.auction_state().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: order_prepared_state.clone(), + auction_accounts: current_state.auction_accounts().cloned(), + }, + _ => current_state.clone(), + } + } } diff --git a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs index a538d9972..56b9abf34 100644 --- a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs +++ b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs @@ -21,7 +21,7 @@ use std::fmt::Display; use std::rc::Rc; use std::str::FromStr; -use super::{Chain, CHAIN_TO_DOMAIN, GUARDIAN_SECRET_KEY}; +use super::{Chain, GUARDIAN_SECRET_KEY}; // Imported from https://github.com/circlefin/solana-cctp-contracts.git rev = "4477f88" @@ -406,9 +406,9 @@ impl CircleAttester { let (recovery_id, compact_sig) = recoverable_signature.serialize_compact(); // Recovery ID goes in byte 65 signature_bytes[0..64].copy_from_slice(&compact_sig); - let recovery_id_try = i32::from(recovery_id) as u8; + let recovery_id_try = u8::try_from(i32::from(recovery_id)).unwrap(); let recovery_id_true = if recovery_id_try < 27 { - recovery_id_try + 27 + recovery_id_try.saturating_add(27) } else { recovery_id_try }; @@ -537,6 +537,7 @@ impl CctpMessage { } } +#[allow(clippy::too_many_arguments)] pub async fn craft_cctp_token_burn_message( test_ctx: &Rc>, source_cctp_domain: u32, @@ -547,7 +548,7 @@ pub async fn craft_cctp_token_burn_message( cctp_mint_recipient: &Pubkey, custodian_address: &Pubkey, ) -> Result { - let destination_cctp_domain = CHAIN_TO_DOMAIN[Chain::Solana as usize].1; // Hard code solana as destination domain + let destination_cctp_domain = Chain::Solana.as_cctp_domain(); // Hard code solana as destination domain assert_eq!(destination_cctp_domain, 5); let message_transmitter_config_data = test_ctx .borrow_mut() @@ -568,7 +569,7 @@ pub async fn craft_cctp_token_burn_message( let burn_message_vec = BurnMessage::format_message( 0, &Pubkey::try_from_slice(&burn_token_address).unwrap(), - &cctp_mint_recipient, + cctp_mint_recipient, amount, &Pubkey::try_from_slice(&[0u8; 32]).unwrap(), )?; @@ -616,12 +617,13 @@ pub async fn craft_cctp_token_burn_message( pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { // Remove '0x' prefix if present - let address_str = eth_address.strip_prefix("0x").unwrap_or(eth_address); + let address_str = eth_address + .strip_prefix("0x") + .unwrap_or_else(|| eth_address); // Decode the hex string to bytes let mut address_bytes = [0u8; 20]; // Ethereum addresses are 20 bytes - hex::decode_to_slice(address_str, &mut address_bytes as &mut [u8]) - .expect("Invalid Ethereum address format"); + hex::decode_to_slice(address_str, &mut address_bytes).expect("Invalid Ethereum address format"); // Create a 32-byte array with leading zeros (Ethereum addresses are padded with zeros on the left) let mut universal_address = [0u8; 32]; @@ -636,8 +638,7 @@ pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { let slow_order_response = liquidity_layer_message .slow_order_response() .expect("Failed to get slow order response"); - let base_fee = slow_order_response.base_fee(); - base_fee + slow_order_response.base_fee() } pub struct UsedNonces; @@ -648,7 +649,10 @@ impl UsedNonces { let first_nonce = if nonce == 0 { 0 } else { - (nonce - 1) / Self::MAX_NONCES * Self::MAX_NONCES + 1 + (nonce.saturating_sub(1)) + .saturating_div(Self::MAX_NONCES) + .saturating_mul(Self::MAX_NONCES) + .saturating_add(1) }; // Could potentially use a more efficient algorithm, but this finds the first nonce in a bucket let remote_domain_converted = remote_domain.to_string(); let first_nonce_converted = first_nonce.to_string(); diff --git a/solana/modules/matching-engine-testing/tests/utils/mod.rs b/solana/modules/matching-engine-testing/tests/utils/mod.rs index 37fbd7628..db70e2aaa 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mod.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mod.rs @@ -6,7 +6,7 @@ pub mod account_fixtures; pub mod airdrop; pub mod auction; -// pub mod cctp_message; +pub mod cctp_message; pub mod constants; pub mod mint; pub mod program_fixtures; diff --git a/solana/modules/matching-engine-testing/tests/utils/setup.rs b/solana/modules/matching-engine-testing/tests/utils/setup.rs index 70f11d01b..16647b2cb 100644 --- a/solana/modules/matching-engine-testing/tests/utils/setup.rs +++ b/solana/modules/matching-engine-testing/tests/utils/setup.rs @@ -246,7 +246,7 @@ impl TestingContext { pub async fn simulate_and_verify_logs( &self, transaction: impl Into, - expected_logs: Vec, + expected_logs: &Vec, ) -> AnyhowResult<()> { let mut ctx = self.test_context.borrow_mut(); let simulation_result = ctx.banks_client.simulate_transaction(transaction).await?; diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index a0e155c51..259faa1e8 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -5,6 +5,6 @@ pub mod close_fast_market_order; pub mod execute_order; pub mod initialise_fast_market_order; pub mod place_initial_offer; -// pub mod prepare_order_response; +pub mod prepare_order_response; pub mod helpers; diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 306ffb0e5..8a79d1ba9 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -65,13 +65,13 @@ impl FinalizedVaaMessage { .unwrap() } - pub fn get_slow_order_response<'a>(&'a self) -> SlowOrderResponse<'a> { + pub fn get_slow_order_response<'a>(&'a self) -> Result> { let liquidity_layer_message: LiquidityLayerDepositMessage<'a> = LiquidityLayerDepositMessage::parse(&self.deposit_payload) - .expect("Cannot get slow order response from deposit payload"); + .map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; let slow_order_response: SlowOrderResponse<'a> = liquidity_layer_message.to_slow_order_response_unchecked(); - slow_order_response + Ok(slow_order_response) } } @@ -232,7 +232,7 @@ pub fn prepare_order_response_cctp_shim( .map_err(|_| MatchingEngineError::InvalidDeposit)?; let deposit = deposit_option .deposit() - .ok_or(MatchingEngineError::InvalidDepositPayloadId)?; + .ok_or_else(|| MatchingEngineError::InvalidDepositPayloadId)?; let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; require_eq!( @@ -261,7 +261,7 @@ pub fn prepare_order_response_cctp_shim( // Construct the finalised vaa message digest data let finalized_vaa_message_digest = { let finalised_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; - let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence - 1; + let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; @@ -429,7 +429,7 @@ pub fn prepare_order_response_cctp_shim( ], data: verify_hash_data, }; - invoke_signed_unchecked(&verify_shim_ix, &accounts, &[])?; + invoke_signed_unchecked(&verify_shim_ix, accounts, &[])?; // End verify deposit message vaa shim // ------------------------------------------------------------------------------------------------ @@ -479,7 +479,7 @@ pub fn prepare_order_response_cctp_shim( }, to_endpoint: to_endpoint_account.info, redeemer_message: fast_market_order_zero_copy.redeemer_message - [..fast_market_order_zero_copy.redeemer_message_length as usize] + [..usize::from(fast_market_order_zero_copy.redeemer_message_length)] .to_vec(), }; // Use cursor in order to write the prepared order response account data diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 860ead502..3270ed81c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -4,8 +4,8 @@ use super::initialise_fast_market_order::{ initialise_fast_market_order, InitialiseFastMarketOrderData, }; use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; -// use super::prepare_order_response::prepare_order_response_cctp_shim; -// use super::prepare_order_response::PrepareOrderResponseCctpShimData; +use super::prepare_order_response::prepare_order_response_cctp_shim; +use super::prepare_order_response::PrepareOrderResponseCctpShimData; use crate::ID; use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; @@ -28,7 +28,7 @@ pub enum FallbackMatchingEngineInstruction<'ix> { CloseFastMarketOrder, PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, - // PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), + PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), } pub fn process_instruction( @@ -53,9 +53,10 @@ pub fn process_instruction( } FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { handle_execute_order_shim(accounts) - } // FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { - // prepare_order_response_cctp_shim(accounts, data) - // } + } + FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { + prepare_order_response_cctp_shim(accounts, data) + } } } @@ -81,11 +82,11 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR => { Some(Self::ExecuteOrderCctpShim) } - // FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { - // Some(Self::PrepareOrderResponseCctpShim( - // PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), - // )) - // } + FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { + Some(Self::PrepareOrderResponseCctpShim( + PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + )) + } _ => None, } } @@ -146,19 +147,20 @@ impl FallbackMatchingEngineInstruction<'_> { ); out - } // Self::PrepareOrderResponseCctpShim(data) => { - // let data_slice = data.to_bytes(); - // let total_capacity = 8 + data_slice.len(); // 8 for the selector, plus the data length + } + Self::PrepareOrderResponseCctpShim(data) => { + let data_slice = data.to_bytes(); + let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length - // let mut out = Vec::with_capacity(total_capacity); + let mut out = Vec::with_capacity(total_capacity); - // out.extend_from_slice( - // &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, - // ); - // out.extend_from_slice(&data_slice); + out.extend_from_slice( + &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, + ); + out.extend_from_slice(&data_slice); - // out - // } + out + } } } } diff --git a/solana/programs/matching-engine/src/lib.rs b/solana/programs/matching-engine/src/lib.rs index b1cd3e6d1..73a895ec2 100644 --- a/solana/programs/matching-engine/src/lib.rs +++ b/solana/programs/matching-engine/src/lib.rs @@ -4,7 +4,7 @@ mod composite; use composite::*; -mod error; +pub mod error; mod events; From a44b42ed6fafef2490b73d42cf0e8abae6c043b5 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 28 Mar 2025 21:39:32 +0000 Subject: [PATCH 045/112] added workflow and idl --- .github/workflows/solana.yml | 8 +- solana/ts/src/idl/json/matching_engine.json | 475 +++++++++++++++++++- solana/ts/src/idl/ts/matching_engine.ts | 475 +++++++++++++++++++- 3 files changed, 953 insertions(+), 5 deletions(-) diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index ffa41cbd0..2aa6261d0 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -127,6 +127,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -136,9 +138,9 @@ jobs: node-version: "20.11.0" solana-cli-version: "1.18.15" anchor-version: "0.30.1" - # - name: Set default Rust toolchain - # run: rustup default stable - # working-directory: ./solana + - name: Set default Rust toolchain + run: rustup default stable + working-directory: ./solana - name: make anchor-test-upgrade run: make anchor-test-upgrade working-directory: ./solana diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index 3b3c04075..8d37b256e 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -1,5 +1,5 @@ { - "address": "", + "address": "MatchingEngine11111111111111111111111111111", "metadata": { "name": "matching_engine", "version": "0.0.0", @@ -961,6 +961,29 @@ ], "args": [] }, + { + "name": "get_cctp_mint_recipient", + "docs": [ + "UNUSED. This instruction does not exist and has never existed. It just reverts and exist to expose an account lol." + ], + "discriminator": [ + 244, + 239, + 207, + 186, + 19, + 125, + 44, + 181 + ], + "accounts": [ + { + "name": "mint_recipient", + "writable": true + } + ], + "args": [] + }, { "name": "improve_offer", "docs": [ @@ -2708,6 +2731,456 @@ ] } ], + "events": [ + { + "name": "AuctionClosed", + "discriminator": [ + 104, + 72, + 168, + 177, + 241, + 79, + 231, + 167 + ] + }, + { + "name": "AuctionSettled", + "discriminator": [ + 61, + 151, + 131, + 170, + 95, + 203, + 219, + 147 + ] + }, + { + "name": "AuctionUpdated", + "discriminator": [ + 67, + 35, + 50, + 236, + 108, + 230, + 253, + 111 + ] + }, + { + "name": "Enacted", + "discriminator": [ + 200, + 226, + 146, + 0, + 188, + 24, + 141, + 143 + ] + }, + { + "name": "FastFillRedeemed", + "discriminator": [ + 192, + 96, + 201, + 180, + 102, + 112, + 34, + 102 + ] + }, + { + "name": "FastFillSequenceReserved", + "discriminator": [ + 6, + 154, + 159, + 87, + 13, + 183, + 211, + 152 + ] + }, + { + "name": "LocalFastOrderFilled", + "discriminator": [ + 131, + 247, + 217, + 194, + 154, + 179, + 238, + 193 + ] + }, + { + "name": "OrderExecuted", + "discriminator": [ + 74, + 135, + 231, + 5, + 168, + 106, + 194, + 117 + ] + }, + { + "name": "Proposed", + "discriminator": [ + 216, + 37, + 138, + 141, + 130, + 208, + 180, + 153 + ] + } + ], + "errors": [ + { + "code": 6002, + "name": "OwnerOnly" + }, + { + "code": 6004, + "name": "OwnerOrAssistantOnly" + }, + { + "code": 6016, + "name": "U64Overflow" + }, + { + "code": 6018, + "name": "U32Overflow" + }, + { + "code": 6032, + "name": "SameEndpoint" + }, + { + "code": 6034, + "name": "InvalidEndpoint" + }, + { + "code": 6048, + "name": "InvalidVaa" + }, + { + "code": 6066, + "name": "InvalidDeposit" + }, + { + "code": 6068, + "name": "InvalidDepositMessage" + }, + { + "code": 6070, + "name": "InvalidPayloadId" + }, + { + "code": 6072, + "name": "InvalidDepositPayloadId" + }, + { + "code": 6074, + "name": "NotFastMarketOrder" + }, + { + "code": 6076, + "name": "VaaMismatch" + }, + { + "code": 6078, + "name": "RedeemerMessageTooLarge" + }, + { + "code": 6096, + "name": "InvalidSourceRouter" + }, + { + "code": 6098, + "name": "InvalidTargetRouter" + }, + { + "code": 6100, + "name": "EndpointDisabled" + }, + { + "code": 6102, + "name": "InvalidCctpEndpoint" + }, + { + "code": 6128, + "name": "Paused" + }, + { + "code": 6256, + "name": "AssistantZeroPubkey" + }, + { + "code": 6257, + "name": "FeeRecipientZeroPubkey" + }, + { + "code": 6258, + "name": "ImmutableProgram" + }, + { + "code": 6260, + "name": "ZeroDuration" + }, + { + "code": 6262, + "name": "ZeroGracePeriod" + }, + { + "code": 6263, + "name": "ZeroPenaltyPeriod" + }, + { + "code": 6264, + "name": "UserPenaltyRewardBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6266, + "name": "InitialPenaltyBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6268, + "name": "MinOfferDeltaBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6270, + "name": "ZeroSecurityDepositBase" + }, + { + "code": 6271, + "name": "SecurityDepositBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6514, + "name": "InvalidNewOwner" + }, + { + "code": 6516, + "name": "AlreadyOwner" + }, + { + "code": 6518, + "name": "NoTransferOwnershipRequest" + }, + { + "code": 6520, + "name": "NotPendingOwner" + }, + { + "code": 6524, + "name": "InvalidChain" + }, + { + "code": 6576, + "name": "ChainNotAllowed" + }, + { + "code": 6578, + "name": "InvalidMintRecipient" + }, + { + "code": 6768, + "name": "ProposalAlreadyEnacted" + }, + { + "code": 6770, + "name": "ProposalDelayNotExpired" + }, + { + "code": 6772, + "name": "InvalidProposal" + }, + { + "code": 6832, + "name": "AuctionConfigMismatch" + }, + { + "code": 7024, + "name": "FastMarketOrderExpired" + }, + { + "code": 7026, + "name": "OfferPriceTooHigh" + }, + { + "code": 7032, + "name": "AuctionNotActive" + }, + { + "code": 7034, + "name": "AuctionPeriodExpired" + }, + { + "code": 7036, + "name": "AuctionPeriodNotExpired" + }, + { + "code": 7044, + "name": "ExecutorTokenMismatch" + }, + { + "code": 7050, + "name": "AuctionNotCompleted" + }, + { + "code": 7054, + "name": "CarpingNotAllowed" + }, + { + "code": 7056, + "name": "AuctionNotSettled" + }, + { + "code": 7058, + "name": "ExecutorNotPreparedBy" + }, + { + "code": 7060, + "name": "InvalidOfferToken" + }, + { + "code": 7062, + "name": "FastFillTooLarge" + }, + { + "code": 7064, + "name": "AuctionExists" + }, + { + "code": 7065, + "name": "NoAuction" + }, + { + "code": 7066, + "name": "BestOfferTokenMismatch" + }, + { + "code": 7068, + "name": "BestOfferTokenRequired" + }, + { + "code": 7070, + "name": "PreparedByMismatch" + }, + { + "code": 7071, + "name": "PreparedOrderResponseNotRequired" + }, + { + "code": 7072, + "name": "AuctionConfigNotRequired" + }, + { + "code": 7073, + "name": "BestOfferTokenNotRequired" + }, + { + "code": 7076, + "name": "FastFillAlreadyRedeemed" + }, + { + "code": 7077, + "name": "FastFillNotRedeemed" + }, + { + "code": 7080, + "name": "ReservedSequenceMismatch" + }, + { + "code": 7082, + "name": "AuctionAlreadySettled" + }, + { + "code": 7084, + "name": "InvalidBaseFeeToken" + }, + { + "code": 7086, + "name": "BaseFeeTokenRequired" + }, + { + "code": 7280, + "name": "CannotCloseAuctionYet" + }, + { + "code": 7282, + "name": "AuctionHistoryNotFull" + }, + { + "code": 7284, + "name": "AuctionHistoryFull" + }, + { + "code": 7536, + "name": "InvalidVerifyVaaShimProgram" + }, + { + "code": 7792, + "name": "AccountAlreadyInitialized" + }, + { + "code": 7794, + "name": "AccountNotWritable" + }, + { + "code": 7796, + "name": "BorshDeserializationError" + }, + { + "code": 7798, + "name": "InvalidPda" + }, + { + "code": 7800, + "name": "AccountDataTooSmall" + }, + { + "code": 7802, + "name": "InvalidProgram" + }, + { + "code": 7804, + "name": "TokenTransferFailed" + }, + { + "code": 7806, + "name": "InvalidMint" + }, + { + "code": 8048, + "name": "SameEndpoints", + "msg": "From and to router endpoints are the same" + }, + { + "code": 8050, + "name": "InvalidCctpMessage" + } + ], "types": [ { "name": "AddCctpRouterEndpointArgs", diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 019ce75bb..2466c267d 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -5,7 +5,7 @@ * IDL can be found at `target/idl/matching_engine.json`. */ export type MatchingEngine = { - "address": "", + "address": "MatchingEngine11111111111111111111111111111", "metadata": { "name": "matchingEngine", "version": "0.0.0", @@ -967,6 +967,29 @@ export type MatchingEngine = { ], "args": [] }, + { + "name": "getCctpMintRecipient", + "docs": [ + "UNUSED. This instruction does not exist and has never existed. It just reverts and exist to expose an account lol." + ], + "discriminator": [ + 244, + 239, + 207, + 186, + 19, + 125, + 44, + 181 + ], + "accounts": [ + { + "name": "mintRecipient", + "writable": true + } + ], + "args": [] + }, { "name": "improveOffer", "docs": [ @@ -2714,6 +2737,456 @@ export type MatchingEngine = { ] } ], + "events": [ + { + "name": "auctionClosed", + "discriminator": [ + 104, + 72, + 168, + 177, + 241, + 79, + 231, + 167 + ] + }, + { + "name": "auctionSettled", + "discriminator": [ + 61, + 151, + 131, + 170, + 95, + 203, + 219, + 147 + ] + }, + { + "name": "auctionUpdated", + "discriminator": [ + 67, + 35, + 50, + 236, + 108, + 230, + 253, + 111 + ] + }, + { + "name": "enacted", + "discriminator": [ + 200, + 226, + 146, + 0, + 188, + 24, + 141, + 143 + ] + }, + { + "name": "fastFillRedeemed", + "discriminator": [ + 192, + 96, + 201, + 180, + 102, + 112, + 34, + 102 + ] + }, + { + "name": "fastFillSequenceReserved", + "discriminator": [ + 6, + 154, + 159, + 87, + 13, + 183, + 211, + 152 + ] + }, + { + "name": "localFastOrderFilled", + "discriminator": [ + 131, + 247, + 217, + 194, + 154, + 179, + 238, + 193 + ] + }, + { + "name": "orderExecuted", + "discriminator": [ + 74, + 135, + 231, + 5, + 168, + 106, + 194, + 117 + ] + }, + { + "name": "proposed", + "discriminator": [ + 216, + 37, + 138, + 141, + 130, + 208, + 180, + 153 + ] + } + ], + "errors": [ + { + "code": 6002, + "name": "ownerOnly" + }, + { + "code": 6004, + "name": "ownerOrAssistantOnly" + }, + { + "code": 6016, + "name": "u64Overflow" + }, + { + "code": 6018, + "name": "u32Overflow" + }, + { + "code": 6032, + "name": "sameEndpoint" + }, + { + "code": 6034, + "name": "invalidEndpoint" + }, + { + "code": 6048, + "name": "invalidVaa" + }, + { + "code": 6066, + "name": "invalidDeposit" + }, + { + "code": 6068, + "name": "invalidDepositMessage" + }, + { + "code": 6070, + "name": "invalidPayloadId" + }, + { + "code": 6072, + "name": "invalidDepositPayloadId" + }, + { + "code": 6074, + "name": "notFastMarketOrder" + }, + { + "code": 6076, + "name": "vaaMismatch" + }, + { + "code": 6078, + "name": "redeemerMessageTooLarge" + }, + { + "code": 6096, + "name": "invalidSourceRouter" + }, + { + "code": 6098, + "name": "invalidTargetRouter" + }, + { + "code": 6100, + "name": "endpointDisabled" + }, + { + "code": 6102, + "name": "invalidCctpEndpoint" + }, + { + "code": 6128, + "name": "paused" + }, + { + "code": 6256, + "name": "assistantZeroPubkey" + }, + { + "code": 6257, + "name": "feeRecipientZeroPubkey" + }, + { + "code": 6258, + "name": "immutableProgram" + }, + { + "code": 6260, + "name": "zeroDuration" + }, + { + "code": 6262, + "name": "zeroGracePeriod" + }, + { + "code": 6263, + "name": "zeroPenaltyPeriod" + }, + { + "code": 6264, + "name": "userPenaltyRewardBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6266, + "name": "initialPenaltyBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6268, + "name": "minOfferDeltaBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6270, + "name": "zeroSecurityDepositBase" + }, + { + "code": 6271, + "name": "securityDepositBpsTooLarge", + "msg": "Value exceeds 1000000" + }, + { + "code": 6514, + "name": "invalidNewOwner" + }, + { + "code": 6516, + "name": "alreadyOwner" + }, + { + "code": 6518, + "name": "noTransferOwnershipRequest" + }, + { + "code": 6520, + "name": "notPendingOwner" + }, + { + "code": 6524, + "name": "invalidChain" + }, + { + "code": 6576, + "name": "chainNotAllowed" + }, + { + "code": 6578, + "name": "invalidMintRecipient" + }, + { + "code": 6768, + "name": "proposalAlreadyEnacted" + }, + { + "code": 6770, + "name": "proposalDelayNotExpired" + }, + { + "code": 6772, + "name": "invalidProposal" + }, + { + "code": 6832, + "name": "auctionConfigMismatch" + }, + { + "code": 7024, + "name": "fastMarketOrderExpired" + }, + { + "code": 7026, + "name": "offerPriceTooHigh" + }, + { + "code": 7032, + "name": "auctionNotActive" + }, + { + "code": 7034, + "name": "auctionPeriodExpired" + }, + { + "code": 7036, + "name": "auctionPeriodNotExpired" + }, + { + "code": 7044, + "name": "executorTokenMismatch" + }, + { + "code": 7050, + "name": "auctionNotCompleted" + }, + { + "code": 7054, + "name": "carpingNotAllowed" + }, + { + "code": 7056, + "name": "auctionNotSettled" + }, + { + "code": 7058, + "name": "executorNotPreparedBy" + }, + { + "code": 7060, + "name": "invalidOfferToken" + }, + { + "code": 7062, + "name": "fastFillTooLarge" + }, + { + "code": 7064, + "name": "auctionExists" + }, + { + "code": 7065, + "name": "noAuction" + }, + { + "code": 7066, + "name": "bestOfferTokenMismatch" + }, + { + "code": 7068, + "name": "bestOfferTokenRequired" + }, + { + "code": 7070, + "name": "preparedByMismatch" + }, + { + "code": 7071, + "name": "preparedOrderResponseNotRequired" + }, + { + "code": 7072, + "name": "auctionConfigNotRequired" + }, + { + "code": 7073, + "name": "bestOfferTokenNotRequired" + }, + { + "code": 7076, + "name": "fastFillAlreadyRedeemed" + }, + { + "code": 7077, + "name": "fastFillNotRedeemed" + }, + { + "code": 7080, + "name": "reservedSequenceMismatch" + }, + { + "code": 7082, + "name": "auctionAlreadySettled" + }, + { + "code": 7084, + "name": "invalidBaseFeeToken" + }, + { + "code": 7086, + "name": "baseFeeTokenRequired" + }, + { + "code": 7280, + "name": "cannotCloseAuctionYet" + }, + { + "code": 7282, + "name": "auctionHistoryNotFull" + }, + { + "code": 7284, + "name": "auctionHistoryFull" + }, + { + "code": 7536, + "name": "invalidVerifyVaaShimProgram" + }, + { + "code": 7792, + "name": "accountAlreadyInitialized" + }, + { + "code": 7794, + "name": "accountNotWritable" + }, + { + "code": 7796, + "name": "borshDeserializationError" + }, + { + "code": 7798, + "name": "invalidPda" + }, + { + "code": 7800, + "name": "accountDataTooSmall" + }, + { + "code": 7802, + "name": "invalidProgram" + }, + { + "code": 7804, + "name": "tokenTransferFailed" + }, + { + "code": 7806, + "name": "invalidMint" + }, + { + "code": 8048, + "name": "sameEndpoints", + "msg": "From and to router endpoints are the same" + }, + { + "code": 8050, + "name": "invalidCctpMessage" + } + ], "types": [ { "name": "addCctpRouterEndpointArgs", From d5f2de4e43157e0cea781be55454319e0752292f Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Sun, 30 Mar 2025 22:42:26 +0100 Subject: [PATCH 046/112] removed Rc> --- .../tests/integration_tests.rs | 157 +++++++++++------- .../tests/shimful/fast_market_order_shim.rs | 14 +- .../tests/shimful/post_message.rs | 9 +- .../tests/shimful/shims_execute_order.rs | 14 +- .../tests/shimful/shims_make_offer.rs | 16 +- .../shimful/shims_prepare_order_response.rs | 18 +- .../tests/shimful/verify_shim.rs | 12 +- .../tests/shimless/execute_order.rs | 10 +- .../tests/shimless/initialize.rs | 14 +- .../tests/shimless/make_offer.rs | 28 ++-- .../tests/shimless/prepare_order_response.rs | 16 +- .../tests/shimless/settle_auction.rs | 10 +- .../tests/testing_engine/config.rs | 2 + .../tests/testing_engine/engine.rs | 78 +++++++-- .../tests/utils/airdrop.rs | 39 ++--- .../tests/utils/auction.rs | 10 +- .../tests/utils/cctp_message.rs | 7 +- .../tests/utils/mint.rs | 13 +- .../tests/utils/router.rs | 61 +++---- .../tests/utils/setup.rs | 120 ++++++------- .../tests/utils/token_account.rs | 50 +++--- .../tests/utils/vaa.rs | 15 +- 22 files changed, 404 insertions(+), 309 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs index ec7ae789c..148a64c1b 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/integration_tests.rs @@ -30,7 +30,7 @@ use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; /// Test that the program is initialised correctly #[tokio::test] pub async fn test_initialize_program() { - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::None, TransferDirection::FromArbitrumToEthereum, None, // Vaa args for creating vaas @@ -42,16 +42,17 @@ pub async fn test_initialize_program() { let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(vec![InstructionTrigger::InitializeProgram( - initialize_config, - )]) + .execute( + &mut test_context, + vec![InstructionTrigger::InitializeProgram(initialize_config)], + ) .await; } /// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains #[tokio::test] pub async fn test_cctp_token_router_endpoint_creation() { - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::None, // Shim mode TransferDirection::FromArbitrumToEthereum, // Transfer direction None, // Vaa args @@ -63,28 +64,35 @@ pub async fn test_cctp_token_router_endpoint_creation() { let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(vec![InstructionTrigger::InitializeProgram( - initialize_config, - )]) + .execute( + &mut test_context, + vec![InstructionTrigger::InitializeProgram(initialize_config)], + ) .await; } #[tokio::test] pub async fn test_local_token_router_endpoint_creation() { - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::None, TransferDirection::FromArbitrumToEthereum, None, ) .await; - let initialize_fixture = - initialize_program(&testing_context, AuctionParametersConfig::default(), None) - .await - .expect("Failed to initialize program"); - + let initialize_fixture = initialize_program( + &testing_context, + &mut test_context, + AuctionParametersConfig::default(), + None, + ) + .await + .expect("Failed to initialize program"); + let payer_signer = testing_context.testing_actors.owner.keypair(); let _local_token_router_endpoint = add_local_router_endpoint_ix( &testing_context, + &mut test_context, + &payer_signer, testing_context.testing_actors.owner.pubkey(), initialize_fixture.get_custodian_address(), testing_context.testing_actors.owner.keypair().as_ref(), @@ -101,25 +109,28 @@ pub async fn test_setup_vaas() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = + let (testing_context, mut test_context) = setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; - testing_context.verify_vaas().await; + testing_context.verify_vaas(&mut test_context).await; let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - ]) + .execute( + &mut test_context, + vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ], + ) .await; } #[tokio::test] pub async fn test_post_message_shims() { - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, None, @@ -128,7 +139,13 @@ pub async fn test_post_message_shims() { let actors = &testing_context.testing_actors; let emitter_signer = actors.owner.keypair(); let payer_signer = actors.solvers[0].keypair(); - set_up_post_message_transaction_test(&testing_context, &payer_signer, &emitter_signer).await; + set_up_post_message_transaction_test( + &testing_context, + &mut test_context, + &payer_signer, + &emitter_signer, + ) + .await; } #[tokio::test] @@ -137,7 +154,7 @@ pub async fn test_initialise_fast_market_order_fallback() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, Some(vaa_args), @@ -152,7 +169,9 @@ pub async fn test_initialise_fast_market_order_fallback() { ]; let testing_engine = TestingEngine::new(testing_context).await; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -161,7 +180,7 @@ pub async fn test_close_fast_market_order_fallback() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, Some(vaa_args), @@ -177,7 +196,9 @@ pub async fn test_close_fast_market_order_fallback() { CloseFastMarketOrderShimInstructionConfig::default(), ), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -186,7 +207,7 @@ pub async fn test_approve_usdc() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, Some(vaa_args), @@ -211,19 +232,16 @@ pub async fn test_approve_usdc() { ) .0; solver - .approve_usdc( - &testing_context.test_context, - &transfer_authority, - offer_price, - ) + .approve_usdc(&mut test_context, &transfer_authority, offer_price) .await; - let usdc_balance = solver.get_balance(&testing_context.test_context).await; + let usdc_balance = solver.get_balance(&mut test_context).await; // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = shimful::verify_shim::create_guardian_signatures( &testing_context, + &mut test_context, &actors.owner.keypair(), &vaa_data, &CORE_BRIDGE_PROGRAM_ID, @@ -234,7 +252,8 @@ pub async fn test_approve_usdc() { println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); - let solver_token_account_info = testing_context + let solver_token_account_info = test_context + .banks_client .get_account(solver_token_account_address) .await .expect("Failed to query banks client for solver token account info") @@ -253,7 +272,7 @@ pub async fn test_place_initial_offer_fallback() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -277,7 +296,9 @@ pub async fn test_place_initial_offer_fallback() { }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -287,7 +308,7 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -317,7 +338,9 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -327,7 +350,7 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -356,7 +379,9 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { ..PlaceInitialOfferInstructionConfig::default() }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -368,7 +393,7 @@ pub async fn test_execute_order_fallback() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -386,7 +411,9 @@ pub async fn test_execute_order_fallback() { InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -396,7 +423,7 @@ pub async fn test_execute_order_shimless() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -411,7 +438,9 @@ pub async fn test_execute_order_shimless() { InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } pub async fn test_execute_order_fallback_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; @@ -419,7 +448,7 @@ pub async fn test_execute_order_fallback_blocks_shimless() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -445,7 +474,9 @@ pub async fn test_execute_order_fallback_blocks_shimless() { ..ExecuteOrderInstructionConfig::default() }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } // From ethereum to arbitrum @@ -456,7 +487,7 @@ pub async fn test_prepare_order_shim_fallback() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -475,7 +506,9 @@ pub async fn test_prepare_order_shim_fallback() { InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } // Prepare order response from ethereum to arbitrum (shimless) @@ -486,7 +519,7 @@ pub async fn test_prepare_order_shimless() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -505,7 +538,9 @@ pub async fn test_prepare_order_shimless() { InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -515,7 +550,7 @@ pub async fn test_prepare_order_response_shimful_blocks_shimless() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -539,7 +574,9 @@ pub async fn test_prepare_order_response_shimful_blocks_shimless() { ..PrepareOrderInstructionConfig::default() }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -549,7 +586,7 @@ pub async fn test_prepare_order_response_shimless_blocks_shimful() { post_vaa: true, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -577,7 +614,9 @@ pub async fn test_prepare_order_response_shimless_blocks_shimful() { ..PrepareOrderInstructionConfig::default() }), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } #[tokio::test] @@ -587,7 +626,7 @@ pub async fn test_settle_auction_complete() { post_vaa: false, ..VaaArgs::default() }; - let testing_context = setup_environment( + let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, Some(vaa_args), @@ -608,5 +647,7 @@ pub async fn test_settle_auction_complete() { InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), ]; - testing_engine.execute(instruction_triggers).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; } diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 3eaa2754d..25394d37f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -15,6 +15,7 @@ use matching_engine::fallback::initialise_fast_market_order::{ }; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, FastMarketOrderParams}; +use solana_program_test::ProgramTestContext; use solana_sdk::transaction::VersionedTransaction; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; use std::rc::Rc; @@ -39,6 +40,7 @@ use wormhole_io::TypePrefixedPayload; /// * The expected error, if any, is reached when executing the instruction pub async fn initialise_fast_market_order_fallback( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, fast_market_order: FastMarketOrderState, guardian_set_pubkey: Pubkey, @@ -55,7 +57,10 @@ pub async fn initialise_fast_market_order_fallback( guardian_signatures_pubkey, guardian_set_bump, ); - let recent_blockhash = testing_context.test_context.borrow().last_blockhash; + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(); let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( &[initialise_fast_market_order_ix], Some(&payer_signer.pubkey()), @@ -64,7 +69,7 @@ pub async fn initialise_fast_market_order_fallback( ); let versioned_transaction = VersionedTransaction::from(transaction); testing_context - .execute_and_verify_transaction(versioned_transaction, expected_error) + .execute_and_verify_transaction(test_context, versioned_transaction, expected_error) .await; } @@ -135,13 +140,14 @@ fn initialise_fast_market_order_fallback_instruction( /// * The expected error, if any, is reached when executing the instruction pub async fn close_fast_market_order_fallback( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, refund_recipient_keypair: &Rc, fast_market_order_address: &Pubkey, expected_error: Option<&ExpectedError>, ) { let program_id = &testing_context.get_matching_engine_program_id(); let recent_blockhash = testing_context - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .expect("Failed to get new blockhash"); let close_fast_market_order_ix = CloseFastMarketOrderFallback { @@ -160,7 +166,7 @@ pub async fn close_fast_market_order_fallback( recent_blockhash, ); testing_context - .execute_and_verify_transaction(transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; } diff --git a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs index 61093a1bd..aa34af823 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs @@ -1,5 +1,6 @@ use crate::utils::{constants::*, setup::TestingContext}; +use solana_program_test::ProgramTestContext; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, hash::Hash, @@ -51,11 +52,12 @@ impl BumpCosts { /// * `emitter_signer` - The emitter signer keypair pub async fn set_up_post_message_transaction_test( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, emitter_signer: &Rc, ) { let recent_blockhash = testing_context - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .expect("Could not get last blockhash"); let (transaction, _bump_costs) = set_up_post_message_transaction( @@ -65,14 +67,11 @@ pub async fn set_up_post_message_transaction_test( recent_blockhash, ); let details = { - let test_ctx = &testing_context.test_context; - let mut ctx = test_ctx.borrow_mut(); - let out = ctx + let out = test_context .banks_client .simulate_transaction(transaction) .await .unwrap(); - drop(ctx); assert!(out.result.clone().unwrap().is_ok(), "{:?}", out.result); out.simulation_details.unwrap() }; diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 7b13d24e7..cc57b4a38 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -8,6 +8,7 @@ use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; +use solana_program_test::ProgramTestContext; use solana_sdk::{ pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction, }; @@ -91,6 +92,7 @@ pub struct ExecuteOrderFallbackFixtureAccounts { pub async fn execute_order_fallback( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, program_id: &Pubkey, solver: Solver, @@ -98,7 +100,6 @@ pub async fn execute_order_fallback( expected_error: Option<&ExpectedError>, ) -> Option { // Get target chain and use as remote address - let test_ctx = &testing_context.test_context; let cctp_message = Pubkey::find_program_address( &[ common::CCTP_MESSAGE_SEED_PREFIX, @@ -181,8 +182,11 @@ pub async fn execute_order_fallback( .instruction(); // Considering fast forwarding blocks here for deadline to be reached - let recent_blockhash = test_ctx.borrow().last_blockhash; - utils::setup::fast_forward_slots(testing_context, 3).await; + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(); + utils::setup::fast_forward_slots(test_context, 3).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), @@ -190,7 +194,7 @@ pub async fn execute_order_fallback( recent_blockhash, ); testing_context - .execute_and_verify_transaction(transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; if expected_error.is_none() { Some(ExecuteOrderFallbackFixture { @@ -212,6 +216,7 @@ pub async fn execute_order_fallback( pub async fn execute_order_fallback_test( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, auction_accounts: &utils::auction::AuctionAccounts, fast_market_order_address: &Pubkey, active_auction_state: &ActiveAuctionState, @@ -231,6 +236,7 @@ pub async fn execute_order_fallback_test( ); execute_order_fallback( testing_context, + test_context, &testing_context.testing_actors.owner.keypair(), &testing_context.get_matching_engine_program_id(), solver, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 4763ea5cd..7148142ff 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -10,6 +10,7 @@ use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShimData as PlaceInitialOfferCctpShimFallbackData, }; use matching_engine::state::Auction; +use solana_program_test::ProgramTestContext; use super::fast_market_order_shim::create_fast_market_order_state_from_vaa_data; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; @@ -39,6 +40,7 @@ use std::rc::Rc; #[allow(clippy::too_many_arguments)] pub async fn place_initial_offer_fallback( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, vaa_data: &utils::vaa::PostedVaaData, solver: Solver, @@ -48,7 +50,6 @@ pub async fn place_initial_offer_fallback( expected_error: Option<&ExpectedError>, ) -> Option { let program_id = testing_context.get_matching_engine_program_id(); - let test_ctx = &testing_context.test_context; let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); let auction_address = Pubkey::find_program_address( @@ -77,10 +78,10 @@ pub async fn place_initial_offer_fallback( .0; solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; - let solver_usdc_balance_before = solver.get_balance(test_ctx).await; + let solver_usdc_balance_before = solver.get_balance(test_context).await; let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price); @@ -106,7 +107,10 @@ pub async fn place_initial_offer_fallback( } .instruction(); - let recent_blockhash = test_ctx.borrow().last_blockhash; + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(); let transaction = Transaction::new_signed_with_payer( &[place_initial_offer_ix], @@ -116,10 +120,10 @@ pub async fn place_initial_offer_fallback( ); testing_context - .execute_and_verify_transaction(transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; if expected_error.is_none() { - let solver_usdc_balance_after = solver.get_balance(test_ctx).await; + let solver_usdc_balance_after = solver.get_balance(test_context).await; assert!( solver_usdc_balance_after < solver_usdc_balance_before, "Solver USDC balance should have decreased" diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index d8e07c2a3..8a0feb7a1 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -16,6 +16,7 @@ use matching_engine::fallback::prepare_order_response::{ }; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; use matching_engine::CCTP_MINT_RECIPIENT; +use solana_program_test::ProgramTestContext; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; @@ -171,13 +172,13 @@ impl PrepareOrderResponseShimDataFixture { pub async fn prepare_order_response_cctp_shim( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, accounts: PrepareOrderResponseShimAccountsFixture, data: PrepareOrderResponseShimDataFixture, matching_engine_program_id: &Pubkey, expected_error: Option<&ExpectedError>, ) -> Option { - let test_ctx = &testing_context.test_context; let fast_market_order_digest = data.fast_market_order.digest(); let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, @@ -247,9 +248,8 @@ pub async fn prepare_order_response_cctp_shim( } .instruction(); - let recent_blockhash = test_ctx - .borrow_mut() - .get_new_latest_blockhash() + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) .await .expect("Failed to get new latest blockhash"); let transaction = Transaction::new_signed_with_payer( @@ -259,7 +259,7 @@ pub async fn prepare_order_response_cctp_shim( recent_blockhash, ); testing_context - .execute_and_verify_transaction(transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; if expected_error.is_none() { Some(PrepareOrderResponseShimFixture { @@ -274,6 +274,7 @@ pub async fn prepare_order_response_cctp_shim( #[allow(clippy::too_many_arguments)] pub async fn prepare_order_response_test( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, deposit_vaa_data: &utils::vaa::PostedVaaData, testing_engine_state: &TestingEngineState, @@ -282,7 +283,6 @@ pub async fn prepare_order_response_test( deposit: &Deposit, expected_error: Option<&ExpectedError>, ) -> Option { - let test_ctx = &testing_context.test_context; let core_bridge_program_id = &testing_context.get_wormhole_program_id(); let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); @@ -296,6 +296,7 @@ pub async fn prepare_order_response_test( let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = super::verify_shim::create_guardian_signatures( testing_context, + test_context, payer_signer, deposit_vaa_data, core_bridge_program_id, @@ -307,7 +308,7 @@ pub async fn prepare_order_response_test( let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { utils::router::get_remote_token_messenger( - testing_context, + test_context, fixture_accounts.ethereum_remote_token_messenger, ) .await @@ -326,7 +327,7 @@ pub async fn prepare_order_response_test( .expect("Custodian address not found"); // TODO: Make checks to see if fast market order sender matches cctp message sender ... let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( - test_ctx, + test_context, source_remote_token_messenger.domain, cctp_nonce, deposit.amount, @@ -371,6 +372,7 @@ pub async fn prepare_order_response_test( ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( testing_context, + test_context, payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, diff --git a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs index 1b509c9c6..c6d00cb5f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs @@ -3,6 +3,7 @@ use crate::utils::{self, setup::TestingContext}; use anchor_lang::prelude::*; use anyhow::Result as AnyhowResult; +use solana_program_test::ProgramTestContext; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, hash::Hash, @@ -33,6 +34,7 @@ use wormhole_svm_shim::verify_vaa; /// * `(guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump)` - The guardian set pubkey, the guardian signatures pubkey and the guardian set bump pub async fn create_guardian_signatures( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, vaa_data: &utils::vaa::PostedVaaData, wormhole_program_id: &Pubkey, @@ -49,6 +51,7 @@ pub async fn create_guardian_signatures( let guardian_set_signatures = vaa_data.sign_with_guardian_key(&guardian_secret_key, 0); let guardian_signatures_pubkey = add_guardian_signatures_account( testing_context, + test_context, payer_signer, guardian_signature_signer, vec![guardian_set_signatures], @@ -79,12 +82,15 @@ pub async fn create_guardian_signatures( /// * `guardian_signatures_pubkey` - The guardian signatures pubkey async fn add_guardian_signatures_account( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, signatures_signer: &Rc, guardian_signatures: Vec<[u8; GUARDIAN_SIGNATURE_LENGTH]>, guardian_set_index: u32, ) -> AnyhowResult { - let new_blockhash = testing_context.get_new_latest_blockhash().await?; + let new_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await?; let transaction = post_signatures_transaction( payer_signer, signatures_signer, @@ -93,7 +99,9 @@ async fn add_guardian_signatures_account( &guardian_signatures, new_blockhash, ); - testing_context.process_transaction(transaction).await?; + testing_context + .process_transaction(test_context, transaction) + .await?; Ok(signatures_signer.pubkey()) } diff --git a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs index a3cdb21c5..22961c21f 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs @@ -15,6 +15,7 @@ use matching_engine::accounts::{ RequiredSysvars, }; use matching_engine::instruction::ExecuteFastOrderCctp as ExecuteOrderShimlessInstruction; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::sysvar::SysvarId; @@ -153,12 +154,13 @@ pub fn create_execute_order_shimless_accounts( pub async fn execute_order_shimless_test( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, auction_accounts: &AuctionAccounts, auction_state: &AuctionState, payer_signer: &Rc, expected_error: Option<&ExpectedError>, ) -> Option { - crate::utils::setup::fast_forward_slots(testing_context, 3).await; + crate::utils::setup::fast_forward_slots(test_context, 3).await; let fixture_accounts = testing_context .get_fixture_accounts() .expect("Fixture accounts not found"); @@ -181,14 +183,12 @@ pub async fn execute_order_shimless_test( Some(&payer_signer.pubkey()), &[payer_signer], testing_context - .test_context - .borrow_mut() - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .unwrap(), ); testing_context - .execute_and_verify_transaction(tx, expected_error) + .execute_and_verify_transaction(test_context, tx, expected_error) .await; if expected_error.is_none() { Some(ExecuteOrderShimlessFixture { diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 5aad91d78..c31dee110 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -1,3 +1,4 @@ +use solana_program_test::ProgramTestContext; use solana_sdk::{ instruction::Instruction, pubkey::Pubkey, @@ -138,10 +139,10 @@ impl From for AuctionParameters { pub async fn initialize_program( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, auction_parameters_config: AuctionParametersConfig, expected_error: Option<&ExpectedError>, ) -> Option { - let test_context = &testing_context.test_context; let program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); @@ -198,14 +199,14 @@ pub async fn initialize_program( }; // Create and sign transaction let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); + Transaction::new_with_payer(&[instruction], Some(&test_context.payer.pubkey())); let new_blockhash = testing_context - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .expect("Could not get new blockhash"); transaction.sign( &[ - &test_context.borrow().payer, + &test_context.payer, &testing_context.testing_actors.owner.keypair(), ], new_blockhash, @@ -214,12 +215,13 @@ pub async fn initialize_program( // Process transaction let versioned_transaction = VersionedTransaction::from(transaction); testing_context - .execute_and_verify_transaction(versioned_transaction, expected_error) + .execute_and_verify_transaction(test_context, versioned_transaction, expected_error) .await; if expected_error.is_none() { // Verify the results - let custodian_account = testing_context + let custodian_account = test_context + .banks_client .get_account(custodian) .await .expect("Failed to get custodian account") diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index 56463ccf4..abd135186 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -16,6 +16,7 @@ use matching_engine::instruction::{ ImproveOffer as ImproveOfferIx, PlaceInitialOfferCctp as PlaceInitialOfferCctpIx, }; use matching_engine::state::Auction; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; use solana_sdk::signature::Keypair; use solana_sdk::signature::Signer; @@ -26,6 +27,7 @@ use utils::vaa::TestVaa; pub async fn place_initial_offer_shimless( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, accounts: &AuctionAccounts, fast_market_order: &TestVaa, offer_price: u64, @@ -33,7 +35,6 @@ pub async fn place_initial_offer_shimless( program_id: Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { - let test_ctx = &testing_context.test_context; let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id, @@ -76,7 +77,8 @@ pub async fn place_initial_offer_shimless( { // Check if solver has already approved usdc let usdc_account = accounts.solver.token_account_address().unwrap(); - let usdc_account_info = testing_context + let usdc_account_info = test_context + .banks_client .get_account(usdc_account) .await .unwrap() @@ -88,14 +90,14 @@ pub async fn place_initial_offer_shimless( if token_account_info.delegate.is_none() { accounts .solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; } else { let delegate = token_account_info.delegate.unwrap(); if delegate != transfer_authority { accounts .solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; } } @@ -139,11 +141,14 @@ pub async fn place_initial_offer_shimless( &[initial_offer_ix_anchor], Some(&payer_signer.pubkey()), &[payer_signer], - test_ctx.borrow().last_blockhash, + testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(), ); testing_context - .execute_and_verify_transaction(tx, expected_error) + .execute_and_verify_transaction(test_context, tx, expected_error) .await; // If the transaction failed and we expected it to pass, we would not get here @@ -171,6 +176,7 @@ pub async fn place_initial_offer_shimless( #[allow(clippy::too_many_arguments)] pub async fn improve_offer( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, program_id: Pubkey, solver: Solver, auction_config: Pubkey, @@ -179,7 +185,6 @@ pub async fn improve_offer( initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, ) -> Option { - let test_ctx = &testing_context.test_context; let active_auction_state = initial_auction_state.get_active_auction().unwrap(); let auction_address = active_auction_state.auction_address; let auction_custody_token_address = active_auction_state.auction_custody_token_address; @@ -197,7 +202,7 @@ pub async fn improve_offer( ) .0; solver - .approve_usdc(test_ctx, &transfer_authority, 420_000__000_000) + .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; let offer_token = solver.token_account_address().unwrap(); @@ -234,11 +239,14 @@ pub async fn improve_offer( &[improve_offer_ix_anchor], Some(&payer_signer.pubkey()), &[payer_signer], - test_ctx.borrow().last_blockhash, + testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(), ); testing_context - .execute_and_verify_transaction(tx, expected_error) + .execute_and_verify_transaction(test_context, tx, expected_error) .await; // If the transaction failed and we expected it to pass, we would not get here diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 00b78d9b6..8bba7e58d 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -18,6 +18,7 @@ use matching_engine::accounts::{ use matching_engine::instruction::PrepareOrderResponseCctp as PrepareOrderResponseCctpIx; use matching_engine::state::PreparedOrderResponse; use matching_engine::CctpMessageArgs; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; @@ -32,6 +33,7 @@ pub struct PrepareOrderResponseFixture { #[allow(clippy::too_many_arguments)] pub async fn prepare_order_response( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, testing_engine_state: &TestingEngineState, to_endpoint_address: &Pubkey, @@ -40,7 +42,6 @@ pub async fn prepare_order_response( expected_error: Option<&ExpectedError>, expected_log_message: Option<&Vec>, ) -> Option { - let test_ctx = &testing_context.test_context; let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); @@ -52,7 +53,7 @@ pub async fn prepare_order_response( let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { utils::router::get_remote_token_messenger( - testing_context, + test_context, fixture_accounts.ethereum_remote_token_messenger, ) .await @@ -77,7 +78,7 @@ pub async fn prepare_order_response( let cctp_nonce = deposit.cctp_nonce; // TODO: Make checks to see if fast market order sender matches cctp message sender ... let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( - test_ctx, + test_context, source_remote_token_messenger.domain, cctp_nonce, deposit.amount, @@ -200,9 +201,8 @@ pub async fn prepare_order_response( &[instruction], Some(&payer_signer.pubkey()), &[payer_signer], - test_ctx - .borrow_mut() - .get_new_latest_blockhash() + testing_context + .get_new_latest_blockhash(test_context) .await .expect("Failed to get new blockhash"), ); @@ -212,12 +212,12 @@ pub async fn prepare_order_response( "Expected error is not allowed when expected log message is provided" ); testing_context - .simulate_and_verify_logs(transaction, expected_log_message) + .simulate_and_verify_logs(test_context, transaction, expected_log_message) .await .unwrap(); } else { testing_context - .execute_and_verify_transaction(transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; } if expected_error.is_none() { diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 61dbc92d1..93519655e 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -7,6 +7,7 @@ use anchor_lang::InstructionData; use anchor_spl::token::spl_token; use matching_engine::accounts::SettleAuctionComplete as SettleAuctionCompleteCpiAccounts; use matching_engine::instruction::SettleAuctionComplete; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; @@ -15,6 +16,7 @@ use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; pub async fn settle_auction_complete( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, payer_signer: &Rc, auction_state: &AuctionState, prepare_order_response_address: &Pubkey, @@ -22,7 +24,6 @@ pub async fn settle_auction_complete( matching_engine_program_id: &Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { - let test_ctx = &testing_context.test_context; let usdc_mint_address = &testing_context.get_usdc_mint_address(); let active_auction = auction_state .get_active_auction() @@ -55,11 +56,14 @@ pub async fn settle_auction_complete( &[settle_auction_complete_ix], Some(&payer_signer.pubkey()), &[&payer_signer], - test_ctx.borrow().last_blockhash, + testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(), ); testing_context - .execute_and_verify_transaction(tx, expected_error) + .execute_and_verify_transaction(test_context, tx, expected_error) .await; if expected_error.is_none() { AuctionState::Settled diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 1cc6e8154..146cd65f1 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -27,6 +27,7 @@ pub struct InitializeInstructionConfig { pub struct CreateCctpRouterEndpointsInstructionConfig { pub chains: HashSet, + pub payer_signer: Option>, pub admin_owner_or_assistant: Option>, pub expected_error: Option, } @@ -35,6 +36,7 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { fn default() -> Self { Self { chains: HashSet::from([Chain::Ethereum, Chain::Arbitrum, Chain::Solana]), + payer_signer: None, admin_owner_or_assistant: None, expected_error: None, } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index d6849bad7..4261bceda 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -1,4 +1,5 @@ use matching_engine::state::FastMarketOrder; +use solana_program_test::ProgramTestContext; use super::{config::*, state::*}; use crate::shimful; @@ -81,59 +82,74 @@ impl TestingEngine { Self { testing_context } } - pub async fn execute(&self, instruction_chain: Vec) { + pub async fn execute( + &self, + test_context: &mut ProgramTestContext, + instruction_chain: Vec, + ) { let mut current_state = self.create_initial_state(); for trigger in instruction_chain { - current_state = self.execute_trigger(¤t_state, &trigger).await; + current_state = self + .execute_trigger(test_context, ¤t_state, &trigger) + .await; } } async fn execute_trigger( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, trigger: &InstructionTrigger, ) -> TestingEngineState { match trigger { InstructionTrigger::InitializeProgram(config) => { - self.initialize_program(current_state, config).await + self.initialize_program(test_context, current_state, config) + .await } InstructionTrigger::CreateCctpRouterEndpoints(config) => { - self.create_cctp_router_endpoints(current_state, config) + self.create_cctp_router_endpoints(test_context, current_state, config) .await } InstructionTrigger::InitializeFastMarketOrderShim(config) => { - self.create_fast_market_order_account(current_state, config) + self.create_fast_market_order_account(test_context, current_state, config) .await } InstructionTrigger::CloseFastMarketOrderShim(config) => { - self.close_fast_market_order_account(current_state, config) + self.close_fast_market_order_account(test_context, current_state, config) .await } InstructionTrigger::PlaceInitialOfferShimless(config) => { - self.place_initial_offer_shimless(current_state, config) + self.place_initial_offer_shimless(test_context, current_state, config) .await } InstructionTrigger::PlaceInitialOfferShim(config) => { - self.place_initial_offer_shim(current_state, config).await + self.place_initial_offer_shim(test_context, current_state, config) + .await } InstructionTrigger::ImproveOfferShimless(config) => { - self.improve_offer_shimless(current_state, config).await + self.improve_offer_shimless(test_context, current_state, config) + .await } InstructionTrigger::ExecuteOrderShim(config) => { - self.execute_order_shim(current_state, config).await + self.execute_order_shim(test_context, current_state, config) + .await } InstructionTrigger::ExecuteOrderShimless(config) => { - self.execute_order_shimless(current_state, config).await + self.execute_order_shimless(test_context, current_state, config) + .await } InstructionTrigger::PrepareOrderShim(config) => { - self.prepare_order_shim(current_state, config).await + self.prepare_order_shim(test_context, current_state, config) + .await } InstructionTrigger::PrepareOrderShimless(config) => { - self.prepare_order_shimless(current_state, config).await + self.prepare_order_shimless(test_context, current_state, config) + .await } InstructionTrigger::SettleAuction(config) => { - self.settle_auction(current_state, config).await + self.settle_auction(test_context, current_state, config) + .await } } } @@ -155,6 +171,7 @@ impl TestingEngine { async fn initialize_program( &self, + test_context: &mut ProgramTestContext, initial_state: &TestingEngineState, config: &InitializeInstructionConfig, ) -> TestingEngineState { @@ -164,6 +181,7 @@ impl TestingEngine { let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { let result = shimless::initialize::initialize_program( &self.testing_context, + test_context, auction_parameters_config, expected_error, ) @@ -203,6 +221,7 @@ impl TestingEngine { async fn create_cctp_router_endpoints( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &CreateCctpRouterEndpointsInstructionConfig, ) -> TestingEngineState { @@ -212,12 +231,18 @@ impl TestingEngine { .expect("Testing state is not initialized"); let custodian_address = initialized_state.custodian_address; let testing_actors = &self.testing_context.testing_actors; + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_actors.owner.keypair()); let admin_owner_or_assistant = config .admin_owner_or_assistant .clone() .unwrap_or_else(|| testing_actors.owner.keypair()); let result = create_all_router_endpoints_test( &self.testing_context, + test_context, + &payer_signer, admin_owner_or_assistant.pubkey(), custodian_address, admin_owner_or_assistant, @@ -233,6 +258,7 @@ impl TestingEngine { async fn create_fast_market_order_account( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &InitializeFastMarketOrderShimInstructionConfig, ) -> TestingEngineState { @@ -251,6 +277,7 @@ impl TestingEngine { let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = create_guardian_signatures( &self.testing_context, + test_context, &payer_signer, &fast_transfer_vaa.vaa_data, &self.testing_context.get_wormhole_program_id(), @@ -270,6 +297,7 @@ impl TestingEngine { initialise_fast_market_order_fallback( &self.testing_context, + test_context, &payer_signer, fast_market_order, guardian_set_pubkey, @@ -301,6 +329,7 @@ impl TestingEngine { async fn close_fast_market_order_account( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &CloseFastMarketOrderShimInstructionConfig, ) -> TestingEngineState { @@ -318,6 +347,7 @@ impl TestingEngine { shimful::fast_market_order_shim::close_fast_market_order_fallback( &self.testing_context, + test_context, &close_account_refund_recipient, &fast_market_order_account, config.expected_error.as_ref(), @@ -336,6 +366,7 @@ impl TestingEngine { } async fn place_initial_offer_shimless( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, ) -> TestingEngineState { @@ -383,6 +414,7 @@ impl TestingEngine { ); let auction_state = shimless::make_offer::place_initial_offer_shimless( &self.testing_context, + test_context, &auction_accounts, fast_vaa, config.offer_price, @@ -395,7 +427,7 @@ impl TestingEngine { auction_state .get_active_auction() .unwrap() - .verify_auction(&self.testing_context) + .verify_auction(&self.testing_context, test_context) .await .expect("Could not verify auction state"); return TestingEngineState::InitialOfferPlaced { @@ -412,6 +444,7 @@ impl TestingEngine { async fn improve_offer_shimless( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &ImproveOfferInstructionConfig, ) -> TestingEngineState { @@ -432,6 +465,7 @@ impl TestingEngine { .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, + test_context, self.testing_context.get_matching_engine_program_id(), solver.clone(), auction_config_address, @@ -457,6 +491,7 @@ impl TestingEngine { async fn place_initial_offer_shim( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, ) -> TestingEngineState { @@ -496,6 +531,7 @@ impl TestingEngine { let place_initial_offer_shim_fixture = shimful::shims_make_offer::place_initial_offer_fallback( &self.testing_context, + test_context, &payer_signer, fast_vaa_data, solver, @@ -512,7 +548,7 @@ impl TestingEngine { .get_active_auction() .unwrap(); active_auction_state - .verify_auction(&self.testing_context) + .verify_auction(&self.testing_context, test_context) .await .expect("Could not verify auction"); return TestingEngineState::InitialOfferPlaced { @@ -529,6 +565,7 @@ impl TestingEngine { async fn execute_order_shim( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { @@ -550,6 +587,7 @@ impl TestingEngine { .expect("Active auction not found"); let result = shimful::shims_execute_order::execute_order_fallback_test( &self.testing_context, + test_context, auction_accounts, &fast_market_order_address, active_auction_state, @@ -580,6 +618,7 @@ impl TestingEngine { async fn execute_order_shimless( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { @@ -613,6 +652,7 @@ impl TestingEngine { ); let result = shimless::execute_order::execute_order_shimless_test( &self.testing_context, + test_context, &auction_accounts, current_state.auction_state(), &payer_signer, @@ -642,6 +682,7 @@ impl TestingEngine { async fn prepare_order_shim( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PrepareOrderInstructionConfig, ) -> TestingEngineState { @@ -665,6 +706,7 @@ impl TestingEngine { let result = shimful::shims_prepare_order_response::prepare_order_response_test( &self.testing_context, + test_context, &payer_signer, deposit_vaa_data, current_state, @@ -696,6 +738,7 @@ impl TestingEngine { async fn prepare_order_shimless( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PrepareOrderInstructionConfig, ) -> TestingEngineState { @@ -716,6 +759,7 @@ impl TestingEngine { .expect("Token account does not exist for solver at index"); let result = shimless::prepare_order_response::prepare_order_response( &self.testing_context, + test_context, &payer_signer, current_state, &auction_accounts.to_router_endpoint, @@ -747,6 +791,7 @@ impl TestingEngine { async fn settle_auction( &self, + test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &SettleAuctionInstructionConfig, ) -> TestingEngineState { @@ -761,6 +806,7 @@ impl TestingEngine { let prepared_order_response = order_prepared_state.prepared_order_address; let auction_state = shimless::settle_auction::settle_auction_complete( &self.testing_context, + test_context, &payer_signer, current_state.auction_state(), &prepared_order_response, diff --git a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs index 32146ec13..643522bfa 100644 --- a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs +++ b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs @@ -2,8 +2,6 @@ use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; use solana_sdk::transaction::{Transaction, VersionedTransaction}; use solana_sdk::{pubkey::Pubkey, signature::Signer, system_instruction}; -use std::cell::RefCell; -use std::rc::Rc; use super::constants; @@ -15,35 +13,31 @@ use super::constants; /// * `recipient` - The recipient of the airdrop /// * `amount` - The amount of SOL to airdrop -pub async fn airdrop( - test_context: &Rc>, - recipient: &Pubkey, - amount: u64, -) { - let mut ctx = test_context.borrow_mut(); - +pub async fn airdrop(test_context: &mut ProgramTestContext, recipient: &Pubkey, amount: u64) { // Create the transfer instruction with values from the context - let transfer_ix = system_instruction::transfer(&ctx.payer.pubkey(), recipient, amount); + let transfer_ix = system_instruction::transfer(&test_context.payer.pubkey(), recipient, amount); // Create and send transaction let tx = Transaction::new_signed_with_payer( &[transfer_ix.clone()], - Some(&ctx.payer.pubkey()), - &[&ctx.payer], - ctx.last_blockhash, + Some(&test_context.payer.pubkey()), + &[&test_context.payer], + test_context.last_blockhash, ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - drop(ctx); + test_context + .banks_client + .process_transaction(tx) + .await + .unwrap(); } pub async fn airdrop_usdc( - test_context: &Rc>, + test_context: &mut ProgramTestContext, recipient_ata: &Pubkey, amount: u64, ) { let new_blockhash = test_context - .borrow_mut() .get_new_latest_blockhash() .await .expect("Failed to get new blockhash"); @@ -52,23 +46,22 @@ pub async fn airdrop_usdc( &spl_token::ID, &usdc_mint_address, recipient_ata, - &test_context.borrow().payer.pubkey(), + &test_context.payer.pubkey(), &[], amount, ) .expect("Failed to create mint to instruction"); let tx = Transaction::new_signed_with_payer( &[mint_to_ix.clone()], - Some(&test_context.borrow().payer.pubkey()), - &[&test_context.borrow().payer], + Some(&test_context.payer.pubkey()), + &[&test_context.payer], new_blockhash, ); let versioned_transaction = VersionedTransaction::from(tx); - let mut ctx = test_context.borrow_mut(); - ctx.banks_client + test_context + .banks_client .process_transaction(versioned_transaction) .await .unwrap(); - drop(ctx); } diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 36d698e99..03c5bb7f9 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::*; +use solana_program_test::ProgramTestContext; use super::router::TestRouterEndpoints; use super::setup::{Solver, TestingContext, TransferDirection}; @@ -91,8 +92,13 @@ impl AuctionAccounts { } impl ActiveAuctionState { - pub async fn verify_auction(&self, testing_context: &TestingContext) -> AnyhowResult<()> { - let auction_account = testing_context + pub async fn verify_auction( + &self, + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + ) -> AnyhowResult<()> { + let auction_account = test_context + .banks_client .get_account(self.auction_address) .await? .expect("Failed to get auction account"); diff --git a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs index 56b9abf34..200f1e85f 100644 --- a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs +++ b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs @@ -16,9 +16,7 @@ use secp256k1::SecretKey as SecpSecretKey; use solana_program::keccak::{Hash, Hasher}; use solana_program_test::ProgramTestContext; use solana_sdk::keccak; -use std::cell::RefCell; use std::fmt::Display; -use std::rc::Rc; use std::str::FromStr; use super::{Chain, GUARDIAN_SECRET_KEY}; @@ -539,7 +537,7 @@ impl CctpMessage { #[allow(clippy::too_many_arguments)] pub async fn craft_cctp_token_burn_message( - test_ctx: &Rc>, + test_context: &mut ProgramTestContext, source_cctp_domain: u32, cctp_nonce: u64, amount: Uint<256, 4>, // Only allows for 8 byte amounts for now. If we want larger amount support, we can change this to uint256. @@ -550,8 +548,7 @@ pub async fn craft_cctp_token_burn_message( ) -> Result { let destination_cctp_domain = Chain::Solana.as_cctp_domain(); // Hard code solana as destination domain assert_eq!(destination_cctp_domain, 5); - let message_transmitter_config_data = test_ctx - .borrow_mut() + let message_transmitter_config_data = test_context .banks_client .get_account(*message_transmitter_config_pubkey) .await diff --git a/solana/modules/matching-engine-testing/tests/utils/mint.rs b/solana/modules/matching-engine-testing/tests/utils/mint.rs index 5b0e4a199..03386bfb4 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mint.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mint.rs @@ -9,11 +9,10 @@ use solana_sdk::{ }; use spl_token::state::Mint; -use std::{cell::RefCell, fs::File, io::Read, path::PathBuf, rc::Rc, str::FromStr}; +use std::{fs::File, io::Read, path::PathBuf, str::FromStr}; #[derive(Clone)] pub struct MintFixture { - pub test_ctx: Rc>, pub key: Pubkey, pub mint: spl_token::state::Mint, pub token_program: Pubkey, @@ -30,15 +29,8 @@ impl MintFixture { /// # Returns /// /// A new MintFixture - pub fn new_from_file( - ctx: &Rc>, - relative_path: &str, - ) -> MintFixture { - let ctx_ref = Rc::clone(ctx); - + pub fn new_from_file(ctx: &mut ProgramTestContext, relative_path: &str) -> MintFixture { let (address, account_info) = { - let mut ctx = ctx.borrow_mut(); - // load cargo workspace path from env let mut path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); path.push(relative_path); @@ -68,7 +60,6 @@ impl MintFixture { let mint = spl_token::state::Mint::unpack(&account_info.data()[..Mint::LEN]).unwrap(); MintFixture { - test_ctx: ctx_ref, key: address, mint, token_program: account_info.owner().to_owned(), diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index a198acf20..b6138b13b 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -19,6 +19,7 @@ use matching_engine::state::RouterEndpoint; use matching_engine::AddCctpRouterEndpointArgs; use matching_engine::LOCAL_CUSTODY_TOKEN_SEED_PREFIX; +use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; @@ -149,14 +150,16 @@ pub fn get_router_endpoint_address(program_id: Pubkey, encoded_chain: &[u8; 2]) pub async fn add_cctp_router_endpoint_ix( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + payer_signer: &Keypair, admin_owner_or_assistant: Pubkey, admin_custodian: Pubkey, admin_keypair: &Keypair, - program_id: Pubkey, remote_token_messenger: Pubkey, chain: Chain, ) -> TestRouterEndpoint { let usdc_mint_address = testing_context.get_usdc_mint_address(); + let program_id = testing_context.get_matching_engine_program_id(); let admin = generate_admin(admin_owner_or_assistant, admin_custodian); let usdc = matching_engine::accounts::Usdc { mint: usdc_mint_address, @@ -172,7 +175,7 @@ pub async fn add_cctp_router_endpoint_ix( .0; let accounts = AddCctpRouterEndpointAccounts { - payer: testing_context.test_context.borrow().payer.pubkey(), + payer: payer_signer.pubkey(), admin, router_endpoint: router_endpoint_address, local_custody_token: local_custody_token_address, @@ -202,27 +205,22 @@ pub async fn add_cctp_router_endpoint_ix( data: ix_data, }; - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&testing_context.test_context.borrow().payer.pubkey()), - ); + let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer_signer.pubkey())); // TODO: Figure out who the signers are let new_blockhash = testing_context - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .expect("Failed to get new blockhash"); - transaction.sign( - &[&testing_context.test_context.borrow().payer, &admin_keypair], - new_blockhash, - ); + transaction.sign(&[payer_signer, admin_keypair], new_blockhash); let versioned_transaction = VersionedTransaction::from(transaction); testing_context - .process_transaction(versioned_transaction) + .process_transaction(test_context, versioned_transaction) .await .expect("Failed to process transaction"); - let endpoint_account = testing_context + let endpoint_account = test_context + .banks_client .get_account(router_endpoint_address) .await .unwrap() @@ -245,11 +243,12 @@ pub async fn add_cctp_router_endpoint_ix( pub async fn add_local_router_endpoint_ix( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + payer_signer: &Keypair, admin_owner_or_assistant: Pubkey, admin_custodian: Pubkey, admin_keypair: &Keypair, ) -> TestRouterEndpoint { - let test_context = &testing_context.test_context; let usdc_mint_address = testing_context.get_usdc_mint_address(); let program_id = testing_context.get_matching_engine_program_id(); let admin = generate_admin(admin_owner_or_assistant, admin_custodian); @@ -272,7 +271,7 @@ pub async fn add_local_router_endpoint_ix( // Create the router endpoint let accounts = AddLocalRouterEndpointAccounts { - payer: test_context.borrow().payer.pubkey(), + payer: payer_signer.pubkey(), admin, router_endpoint: router_endpoint_address, local: local_token_router, @@ -287,24 +286,21 @@ pub async fn add_local_router_endpoint_ix( data: ix_data, }; - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&test_context.borrow().payer.pubkey())); + let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer_signer.pubkey())); let new_blockhash = testing_context - .get_new_latest_blockhash() + .get_new_latest_blockhash(test_context) .await .expect("Could not get new blockhash"); - transaction.sign( - &[&test_context.borrow().payer, &admin_keypair], - new_blockhash, - ); + transaction.sign(&[payer_signer, admin_keypair], new_blockhash); let versioned_transaction = VersionedTransaction::from(transaction); testing_context - .process_transaction(versioned_transaction) + .process_transaction(test_context, versioned_transaction) .await .expect("Failed to process transaction"); - let endpoint_account = testing_context + let endpoint_account = test_context + .banks_client .get_account(router_endpoint_address) .await .expect("Failed to get account") @@ -327,13 +323,14 @@ pub async fn add_local_router_endpoint_ix( pub async fn create_cctp_router_endpoint( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + payer_signer: &Keypair, admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, chain: Chain, ) -> TestRouterEndpoint { let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); - let program_id = testing_context.get_matching_engine_program_id(); let remote_token_messenger = match chain { Chain::Arbitrum => fixture_accounts.arbitrum_remote_token_messenger, Chain::Ethereum => fixture_accounts.ethereum_remote_token_messenger, @@ -344,10 +341,11 @@ pub async fn create_cctp_router_endpoint( add_cctp_router_endpoint_ix( testing_context, + test_context, + payer_signer, admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), - program_id, remote_token_messenger, chain, ) @@ -356,6 +354,8 @@ pub async fn create_cctp_router_endpoint( pub async fn create_all_router_endpoints_test( testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + payer_signer: &Keypair, admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, @@ -367,6 +367,8 @@ pub async fn create_all_router_endpoints_test( Chain::Solana => { let local_token_router_endpoint = add_local_router_endpoint_ix( testing_context, + test_context, + payer_signer, admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), @@ -377,6 +379,8 @@ pub async fn create_all_router_endpoints_test( Chain::Arbitrum | Chain::Ethereum => { let cctp_router_endpoint = create_cctp_router_endpoint( testing_context, + test_context, + payer_signer, admin_owner_or_assistant, custodian_address, admin_keypair.clone(), @@ -394,10 +398,11 @@ pub async fn create_all_router_endpoints_test( } pub async fn get_remote_token_messenger( - testing_context: &TestingContext, + test_context: &mut ProgramTestContext, address: Pubkey, ) -> RemoteTokenMessenger { - let remote_token_messenger_data = testing_context + let remote_token_messenger_data = test_context + .banks_client .get_account(address) .await .unwrap() diff --git a/solana/modules/matching-engine-testing/tests/utils/setup.rs b/solana/modules/matching-engine-testing/tests/utils/setup.rs index 16647b2cb..ee3e50f77 100644 --- a/solana/modules/matching-engine-testing/tests/utils/setup.rs +++ b/solana/modules/matching-engine-testing/tests/utils/setup.rs @@ -31,7 +31,6 @@ use solana_sdk::{ signature::{Keypair, Signer}, transaction::Transaction, }; -use std::cell::RefCell; use std::rc::Rc; // Configures the program ID and CCTP mint recipient based on the environment @@ -123,9 +122,8 @@ impl PreTestingContext { } pub struct TestingContext { - pub program_data_account: Pubkey, // TODO: Move this into something smarter + pub program_data_account: Pubkey, pub testing_actors: TestingActors, - pub test_context: Rc>, pub fixture_accounts: Option, pub testing_state: TestingState, } @@ -135,24 +133,22 @@ impl TestingContext { mut pre_testing_context: PreTestingContext, transfer_direction: TransferDirection, vaas_test: Option, - ) -> Self { - let test_context = Rc::new(RefCell::new( - pre_testing_context.program_test.start_with_context().await, - )); + ) -> (Self, ProgramTestContext) { + let mut test_context = pre_testing_context.program_test.start_with_context().await; // Airdrop to all actors pre_testing_context .testing_actors - .airdrop_all(&test_context) + .airdrop_all(&mut test_context) .await; // Create USDC mint - let _mint_fixture = MintFixture::new_from_file(&test_context, USDC_MINT_FIXTURE_PATH); + let _mint_fixture = MintFixture::new_from_file(&mut test_context, USDC_MINT_FIXTURE_PATH); // Create USDC ATAs for all actors that need them pre_testing_context .testing_actors - .create_atas(&test_context, USDC_MINT_ADDRESS) + .create_atas(&mut test_context, USDC_MINT_ADDRESS) .await; let testing_state = match vaas_test { Some(vaas_test) => TestingState { @@ -165,17 +161,22 @@ impl TestingContext { ..TestingState::default() }, }; - TestingContext { - program_data_account: pre_testing_context.program_data_pubkey, - testing_actors: pre_testing_context.testing_actors, + ( + TestingContext { + program_data_account: pre_testing_context.program_data_pubkey, + testing_actors: pre_testing_context.testing_actors, + fixture_accounts: Some(pre_testing_context.account_fixtures), + testing_state, + }, test_context, - fixture_accounts: Some(pre_testing_context.account_fixtures), - testing_state, - } + ) } - pub async fn verify_vaas(&self) { - self.testing_state.vaas.verify_posted_vaas(self).await; + pub async fn verify_vaas(&self, test_context: &mut ProgramTestContext) { + self.testing_state + .vaas + .verify_posted_vaas(test_context) + .await; } pub fn get_vaa_pair(&self, index: usize) -> Option { @@ -206,32 +207,24 @@ impl TestingContext { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } - pub async fn get_new_latest_blockhash(&self) -> AnyhowResult { - let mut ctx = self.test_context.borrow_mut(); - let handle = ctx.get_new_latest_blockhash(); + pub async fn get_new_latest_blockhash( + &self, + test_context: &mut ProgramTestContext, + ) -> AnyhowResult { + let handle = test_context.get_new_latest_blockhash(); let hash = handle.await?; Ok(hash) } pub async fn process_transaction( &self, + test_context: &mut ProgramTestContext, transaction: impl Into, ) -> Result<(), BanksClientError> { - let mut ctx = self.test_context.borrow_mut(); - let handle = ctx.banks_client.process_transaction(transaction); + let handle = test_context.banks_client.process_transaction(transaction); handle.await } - pub async fn get_account( - &self, - address: Pubkey, - ) -> Result, BanksClientError> { - let mut ctx = self.test_context.borrow_mut(); - let account = ctx.banks_client.get_account(address).await?; - drop(ctx); - Ok(account) - } - /// Simulates a transaction and verifies that the logs contain the expected lines /// /// # Arguments @@ -245,12 +238,14 @@ impl TestingContext { #[allow(dead_code)] pub async fn simulate_and_verify_logs( &self, + test_context: &mut ProgramTestContext, transaction: impl Into, expected_logs: &Vec, ) -> AnyhowResult<()> { - let mut ctx = self.test_context.borrow_mut(); - let simulation_result = ctx.banks_client.simulate_transaction(transaction).await?; - drop(ctx); + let simulation_result = test_context + .banks_client + .simulate_transaction(transaction) + .await?; // Verify the transaction succeeded assert!( @@ -284,10 +279,11 @@ impl TestingContext { // TODO: Edit to handle multiple instructions in a single transaction pub async fn execute_and_verify_transaction( &self, + test_context: &mut ProgramTestContext, transaction: impl Into, expected_error: Option<&ExpectedError>, ) { - let tx_result = self.process_transaction(transaction).await; + let tx_result = self.process_transaction(test_context, transaction).await; if let Some(expected_error) = expected_error { let tx_error = tx_result.expect_err(&format!( "Expected error {:?}, but transaction succeeded", @@ -353,17 +349,15 @@ impl Solver { pub async fn approve_usdc( &self, - test_context: &Rc>, + test_context: &mut ProgramTestContext, delegate: &Pubkey, amount: u64, ) { // If signer pubkeys are empty, it means that the owner is the signer - let mut ctx = test_context.borrow_mut(); - let last_blockhash = ctx + let last_blockhash = test_context .get_new_latest_blockhash() .await .expect("Failed to get new blockhash"); - drop(ctx); let approve_ix = approve( &spl_token::ID, &self.token_account_address().unwrap(), @@ -379,15 +373,14 @@ impl Solver { &[&self.actor.keypair()], last_blockhash, ); - let mut ctx = test_context.borrow_mut(); - ctx.banks_client + test_context + .banks_client .process_transaction(transaction) .await .expect("Failed to approve USDC"); - drop(ctx); } - pub async fn get_balance(&self, test_context: &Rc>) -> u64 { + pub async fn get_balance(&self, test_context: &mut ProgramTestContext) -> u64 { self.actor.get_balance(test_context).await } } @@ -427,10 +420,9 @@ impl TestingActor { self.token_account.as_ref().map(|t| t.address) } - pub async fn get_balance(&self, test_context: &Rc>) -> u64 { + pub async fn get_balance(&self, test_context: &mut ProgramTestContext) -> u64 { if let Some(token_account) = self.token_account_address() { let account = test_context - .borrow_mut() .banks_client .get_account(token_account) .await @@ -490,7 +482,7 @@ impl TestingActors { } /// Transfer Lamports to Executors - async fn airdrop_all(&self, test_context: &Rc>) { + async fn airdrop_all(&self, test_context: &mut ProgramTestContext) { airdrop(test_context, &self.owner.pubkey(), 10000000000).await; airdrop(test_context, &self.owner_assistant.pubkey(), 10000000000).await; airdrop(test_context, &self.fee_recipient.pubkey(), 10000000000).await; @@ -504,13 +496,12 @@ impl TestingActors { /// Set up ATAs for Various Owners async fn create_atas( &mut self, - test_context: &Rc>, + test_context: &mut ProgramTestContext, usdc_mint_address: Pubkey, ) { for actor in self.token_account_actors() { let usdc_ata = - create_token_account(test_context.clone(), &actor.keypair(), &usdc_mint_address) - .await; + create_token_account(test_context, &actor.keypair(), &usdc_mint_address).await; airdrop_usdc(test_context, &usdc_ata.address, 420_000__000_000).await; actor.token_account = Some(usdc_ata); } @@ -520,14 +511,13 @@ impl TestingActors { #[allow(dead_code)] async fn add_solvers( &mut self, - test_context: &Rc>, + test_context: &mut ProgramTestContext, num_solvers: usize, usdc_mint_address: Pubkey, ) { for _ in 0..num_solvers { let keypair = Rc::new(Keypair::new()); - let usdc_ata = - create_token_account(test_context.clone(), &keypair, &usdc_mint_address).await; + let usdc_ata = create_token_account(test_context, &keypair, &usdc_mint_address).await; airdrop(test_context, &keypair.pubkey(), 10000000000).await; self.solvers .push(Solver::new(keypair.clone(), Some(usdc_ata))); @@ -535,40 +525,30 @@ impl TestingActors { } } -pub async fn fast_forward_slots(testing_context: &TestingContext, num_slots: u64) { +pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots: u64) { // Get the current slot - let mut current_slot = testing_context - .test_context - .borrow_mut() - .banks_client - .get_root_slot() - .await - .unwrap(); - - let test_context = &testing_context.test_context; + let mut current_slot = test_context.banks_client.get_root_slot().await.unwrap(); let target_slot = current_slot.saturating_add(num_slots); while current_slot < target_slot { // Warp to the next slot - note we need to borrow_mut() here test_context - .borrow_mut() .warp_to_slot(current_slot.saturating_add(1)) .expect("Failed to warp to slot"); current_slot = current_slot.saturating_add(1); } // Optionally, process a transaction to ensure the new slot is recognized - let recent_blockhash = test_context.borrow().last_blockhash; - let payer = test_context.borrow().payer.pubkey(); + let recent_blockhash = test_context.last_blockhash; + let payer = test_context.payer.pubkey(); let tx = Transaction::new_signed_with_payer( &[], Some(&payer), - &[&test_context.borrow().payer], + &[&test_context.payer], recent_blockhash, ); test_context - .borrow_mut() .banks_client .process_transaction(tx) .await @@ -638,7 +618,7 @@ pub async fn setup_environment( shim_mode: ShimMode, transfer_direction: TransferDirection, vaa_args: Option, -) -> TestingContext { +) -> (TestingContext, ProgramTestContext) { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); let vaas_test: Option = match vaa_args { Some(vaa_args) => { diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index 9f9b1f25f..8e1ac5da7 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -5,11 +5,10 @@ use solana_sdk::{ program_pack::Pack, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction, }; -use std::{cell::RefCell, fs, rc::Rc}; +use std::fs; #[derive(Clone)] pub struct TokenAccountFixture { - pub test_ctx: Rc>, pub address: Pubkey, pub account: spl_token::state::Account, } @@ -33,80 +32,75 @@ impl std::fmt::Debug for TokenAccountFixture { /// * `owner` - The owner of the account /// * `mint` - The mint of the account pub async fn create_token_account( - test_ctx: Rc>, + test_ctx: &mut ProgramTestContext, owner: &Keypair, mint: &Pubkey, ) -> TokenAccountFixture { - let test_ctx_ref = Rc::clone(&test_ctx); - // Derive the Associated Token Account (ATA) for fee_recipient let token_account_address = spl_associated_token_account::get_associated_token_address(&owner.pubkey(), mint); // Inspired by https://github.com/mrgnlabs/marginfi-v2/blob/3b7bf0aceb684a762c8552412001c8d355033119/test-utils/src/spl.rs#L56 let token_account = { - let mut ctx = test_ctx.borrow_mut(); - // Create instruction using borrowed values let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( - &ctx.payer.pubkey(), // Funding account - &owner.pubkey(), // Wallet address - mint, // Mint address - &spl_token::id(), // Token program + &test_ctx.payer.pubkey(), // Funding account + &owner.pubkey(), // Wallet address + mint, // Mint address + &spl_token::id(), // Token program ); // Create and process transaction let tx = Transaction::new_signed_with_payer( &[create_ata_ix], - Some(&ctx.payer.pubkey()), - &[&ctx.payer], - ctx.last_blockhash, + Some(&test_ctx.payer.pubkey()), + &[&test_ctx.payer], + test_ctx.last_blockhash, ); - ctx.banks_client.process_transaction(tx).await.unwrap(); + test_ctx.banks_client.process_transaction(tx).await.unwrap(); // Get the account - ctx.banks_client + test_ctx + .banks_client .get_account(token_account_address) .await .unwrap() .unwrap_or_else(|| panic!("Failed to get token account")) }; TokenAccountFixture { - test_ctx: test_ctx_ref, address: token_account_address, account: spl_token::state::Account::unpack(&token_account.data).unwrap(), } } pub async fn create_token_account_for_pda( - test_context: &Rc>, + test_context: &mut ProgramTestContext, pda: &Pubkey, // The PDA that will own the token account mint: &Pubkey, // The mint (USDC in your case) ) -> Pubkey { - let mut ctx = test_context.borrow_mut(); - // Get the ATA address let ata = anchor_spl::associated_token::get_associated_token_address(pda, mint); // Create the create_ata instruction let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( - &ctx.payer.pubkey(), // Funding account - pda, // Account that will own the token account - mint, // Token mint (USDC) - &spl_token::id(), // Token program + &test_context.payer.pubkey(), // Funding account + pda, // Account that will own the token account + mint, // Token mint (USDC) + &spl_token::id(), // Token program ); // Create and send transaction let transaction = Transaction::new_signed_with_payer( &[create_ata_ix], - Some(&ctx.payer.pubkey()), - &[&ctx.payer], - ctx.last_blockhash, + Some(&test_context.payer.pubkey()), + &[&test_context.payer], + test_context.last_blockhash, ); - ctx.banks_client + test_context + .banks_client .process_transaction(transaction) .await .unwrap(); diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index a39d82e8f..d56139150 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -6,13 +6,12 @@ use secp256k1::SecretKey as SecpSecretKey; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use super::constants::Chain; -use super::setup::TestingContext; use super::constants::CORE_BRIDGE_PID; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use solana_program::keccak; -use solana_program_test::ProgramTest; +use solana_program_test::{ProgramTest, ProgramTestContext}; use solana_sdk::account::Account; use std::ops::{Deref, DerefMut}; @@ -358,11 +357,12 @@ impl TestVaaPair { .create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey); } - pub async fn verify_posted_vaa_pair(&self, testing_context: &TestingContext) { + pub async fn verify_posted_vaa_pair(&self, test_context: &mut ProgramTestContext) { let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); { - let deposit_vaa = testing_context + let deposit_vaa = test_context + .banks_client .get_account(self.deposit_vaa.vaa_pubkey) .await .unwrap(); @@ -374,7 +374,8 @@ impl TestVaaPair { } { - let fast_transfer_vaa = testing_context + let fast_transfer_vaa = test_context + .banks_client .get_account(self.fast_transfer_vaa.vaa_pubkey) .await .unwrap(); @@ -571,10 +572,10 @@ impl TestVaaPairs { } } - pub async fn verify_posted_vaas(&self, testing_context: &TestingContext) { + pub async fn verify_posted_vaas(&self, test_context: &mut ProgramTestContext) { for vaa_pair in self.0.iter() { if vaa_pair.is_posted() { - vaa_pair.verify_posted_vaa_pair(testing_context).await; + vaa_pair.verify_posted_vaa_pair(test_context).await; } } } From 7263e6582d76e22fa38024f219a93c6519006094 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Sun, 30 Mar 2025 23:49:27 +0100 Subject: [PATCH 047/112] made lint --- .../tests/integration_tests.rs | 21 ++--- .../tests/shimful/fast_market_order_shim.rs | 22 ++--- .../shimful/shims_prepare_order_response.rs | 34 ++++--- .../tests/shimful/verify_shim.rs | 12 ++- .../tests/shimless/make_offer.rs | 7 +- .../tests/shimless/settle_auction.rs | 8 +- .../tests/testing_engine/engine.rs | 38 +++----- .../tests/utils/router.rs | 8 +- .../tests/utils/vaa.rs | 92 ++++++++++++------- 9 files changed, 126 insertions(+), 116 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs index 148a64c1b..3743431d4 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/integration_tests.rs @@ -238,17 +238,16 @@ pub async fn test_approve_usdc() { let usdc_balance = solver.get_balance(&mut test_context).await; // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. - let (_guardian_set_pubkey, _guardian_signatures_pubkey, _guardian_set_bump) = - shimful::verify_shim::create_guardian_signatures( - &testing_context, - &mut test_context, - &actors.owner.keypair(), - &vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - None, - ) - .await - .expect("Failed to create guardian signatures"); + let _guardian_signature_info = shimful::verify_shim::create_guardian_signatures( + &testing_context, + &mut test_context, + &actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await + .expect("Failed to create guardian signatures"); println!("Solver USDC balance: {:?}", usdc_balance); let solver_token_account_address = solver.token_account_address().unwrap(); diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 25394d37f..e5d9c34c4 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -3,6 +3,7 @@ use crate::testing_engine::config::ExpectedError; use super::super::utils; use super::super::utils::constants::*; use super::super::utils::setup::TestingContext; +use super::verify_shim::GuardianSignatureInfo; use common::messages::FastMarketOrder; use matching_engine::fallback::close_fast_market_order::{ CloseFastMarketOrder as CloseFastMarketOrderFallback, @@ -43,9 +44,7 @@ pub async fn initialise_fast_market_order_fallback( test_context: &mut ProgramTestContext, payer_signer: &Rc, fast_market_order: FastMarketOrderState, - guardian_set_pubkey: Pubkey, - guardian_signatures_pubkey: Pubkey, - guardian_set_bump: u8, + guardian_signature_info: &GuardianSignatureInfo, expected_error: Option<&ExpectedError>, ) { let program_id = &testing_context.get_matching_engine_program_id(); @@ -53,9 +52,7 @@ pub async fn initialise_fast_market_order_fallback( payer_signer, program_id, fast_market_order, - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, + guardian_signature_info, ); let recent_blockhash = testing_context .get_new_latest_blockhash(test_context) @@ -93,9 +90,7 @@ fn initialise_fast_market_order_fallback_instruction( payer_signer: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, - guardian_set_pubkey: Pubkey, - guardian_signatures_pubkey: Pubkey, - guardian_set_bump: u8, + guardian_signature_info: &GuardianSignatureInfo, ) -> solana_program::instruction::Instruction { let fast_market_order_account = Pubkey::find_program_address( &[ @@ -110,8 +105,8 @@ fn initialise_fast_market_order_fallback_instruction( let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { signer: &payer_signer.pubkey(), fast_market_order_account: &fast_market_order_account, - guardian_set: &guardian_set_pubkey, - guardian_set_signatures: &guardian_signatures_pubkey, + guardian_set: &guardian_signature_info.guardian_set_pubkey, + guardian_set_signatures: &guardian_signature_info.guardian_signatures_pubkey, verify_vaa_shim_program: &WORMHOLE_VERIFY_VAA_SHIM_PID, system_program: &solana_program::system_program::ID, }; @@ -119,7 +114,10 @@ fn initialise_fast_market_order_fallback_instruction( InitialiseFastMarketOrderFallback { program_id, accounts: create_fast_market_order_accounts, - data: InitialiseFastMarketOrderFallbackData::new(fast_market_order, guardian_set_bump), + data: InitialiseFastMarketOrderFallbackData::new( + fast_market_order, + guardian_signature_info.guardian_set_bump, + ), } .instruction() } diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index 8a0feb7a1..db6f5ec8b 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -3,6 +3,7 @@ use crate::testing_engine::state::TestingEngineState; use crate::utils::setup::{TestingContext, TransferDirection}; use super::super::utils; +use super::verify_shim::GuardianSignatureInfo; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use common::wormhole_cctp_solana::cctp::{ @@ -62,8 +63,7 @@ impl PrepareOrderResponseShimAccountsFixture { to_router_endpoint: &Pubkey, usdc_mint_address: &Pubkey, cctp_message_decoded: &CctpMessageDecoded, - guardian_set: &Pubkey, - guardian_set_signatures: &Pubkey, + guardian_signature_info: &GuardianSignatureInfo, transfer_direction: &TransferDirection, ) -> Self { let cctp_message_transmitter_event_authority = @@ -117,8 +117,8 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, cctp_message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority, - guardian_set: *guardian_set, - guardian_set_signatures: *guardian_set_signatures, + guardian_set: guardian_signature_info.guardian_set_pubkey, + guardian_set_signatures: guardian_signature_info.guardian_signatures_pubkey, } } } @@ -293,17 +293,16 @@ pub async fn prepare_order_response_test( .clone() .expect("Fixture accounts not found"); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - super::verify_shim::create_guardian_signatures( - testing_context, - test_context, - payer_signer, - deposit_vaa_data, - core_bridge_program_id, - None, - ) - .await - .unwrap(); + let guardian_signature_info = super::verify_shim::create_guardian_signatures( + testing_context, + test_context, + payer_signer, + deposit_vaa_data, + core_bridge_program_id, + None, + ) + .await + .unwrap(); let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { @@ -350,7 +349,7 @@ pub async fn prepare_order_response_test( deposit, deposit_base_fee, &fast_market_order_state, - guardian_set_bump, + guardian_signature_info.guardian_set_bump, ); let fast_market_order_address = testing_engine_state .fast_market_order() @@ -366,8 +365,7 @@ pub async fn prepare_order_response_test( to_endpoint_address, usdc_mint_address, &cctp_message_decoded, - &guardian_set_pubkey, - &guardian_signatures_pubkey, + &guardian_signature_info, &testing_context.testing_state.transfer_direction, ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( diff --git a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs index c6d00cb5f..1f2c7e7a2 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs @@ -17,6 +17,12 @@ use std::str::FromStr; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use wormhole_svm_shim::verify_vaa; +pub struct GuardianSignatureInfo { + pub guardian_set_pubkey: Pubkey, + pub guardian_signatures_pubkey: Pubkey, + pub guardian_set_bump: u8, +} + /// Create guardian signatures for a given vaa data /// /// This also creates the account holding the signatures and posts the signatures to the guardian signatures account @@ -39,7 +45,7 @@ pub async fn create_guardian_signatures( vaa_data: &utils::vaa::PostedVaaData, wormhole_program_id: &Pubkey, guardian_signature_signer: Option<&Rc>, -) -> AnyhowResult<(Pubkey, Pubkey, u8)> { +) -> AnyhowResult { let new_keypair = Rc::new(Keypair::new()); let guardian_signature_signer = guardian_signature_signer.unwrap_or_else(|| &new_keypair); let (guardian_set_pubkey, guardian_set_bump) = @@ -58,11 +64,11 @@ pub async fn create_guardian_signatures( 0, ) .await?; - Ok(( + Ok(GuardianSignatureInfo { guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump, - )) + }) } /// Add a guardian signatures account diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index abd135186..2c7cd1aab 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -32,9 +32,9 @@ pub async fn place_initial_offer_shimless( fast_market_order: &TestVaa, offer_price: u64, payer_signer: &Rc, - program_id: Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { + let program_id = testing_context.get_matching_engine_program_id(); let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], &program_id, @@ -173,19 +173,18 @@ pub async fn place_initial_offer_shimless( } } -#[allow(clippy::too_many_arguments)] pub async fn improve_offer( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - program_id: Pubkey, solver: Solver, - auction_config: Pubkey, offer_price: u64, payer_signer: &Rc, initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, ) -> Option { + let program_id = testing_context.get_matching_engine_program_id(); let active_auction_state = initial_auction_state.get_active_auction().unwrap(); + let auction_config = active_auction_state.auction_config_address; let auction_address = active_auction_state.auction_address; let auction_custody_token_address = active_auction_state.auction_custody_token_address; diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 93519655e..a356a9ab2 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -21,9 +21,9 @@ pub async fn settle_auction_complete( auction_state: &AuctionState, prepare_order_response_address: &Pubkey, prepared_custody_token: &Pubkey, - matching_engine_program_id: &Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { + let matching_engine_program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); let active_auction = auction_state .get_active_auction() @@ -31,7 +31,7 @@ pub async fn settle_auction_complete( let base_fee_token = *usdc_mint_address; let event_seeds = EVENT_AUTHORITY_SEED; let event_authority = - Pubkey::find_program_address(&[event_seeds], matching_engine_program_id).0; + Pubkey::find_program_address(&[event_seeds], &matching_engine_program_id).0; let settle_auction_accounts = SettleAuctionCompleteCpiAccounts { beneficiary: payer_signer.pubkey(), base_fee_token, @@ -41,13 +41,13 @@ pub async fn settle_auction_complete( best_offer_token: active_auction.best_offer.offer_token, token_program: spl_token::ID, event_authority, - program: *matching_engine_program_id, + program: matching_engine_program_id, }; let settle_auction_complete_cpi = SettleAuctionComplete {}; let settle_auction_complete_ix = Instruction { - program_id: *matching_engine_program_id, + program_id: matching_engine_program_id, accounts: settle_auction_accounts.to_account_metas(Some(false)), data: settle_auction_complete_cpi.data(), }; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 4261bceda..3d2f98105 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -19,7 +19,6 @@ use crate::utils::{ setup::TestingContext, }; use anchor_lang::prelude::*; -use solana_sdk::signature::Signer; #[allow(dead_code)] pub enum InstructionTrigger { @@ -243,7 +242,6 @@ impl TestingEngine { &self.testing_context, test_context, &payer_signer, - admin_owner_or_assistant.pubkey(), custodian_address, admin_owner_or_assistant, config.chains.clone(), @@ -274,17 +272,16 @@ impl TestingEngine { .payer_signer .clone() .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); - let (guardian_set_pubkey, guardian_signatures_pubkey, guardian_set_bump) = - create_guardian_signatures( - &self.testing_context, - test_context, - &payer_signer, - &fast_transfer_vaa.vaa_data, - &self.testing_context.get_wormhole_program_id(), - None, - ) - .await - .expect("Failed to create guardian signatures"); + let guardian_signature_info = create_guardian_signatures( + &self.testing_context, + test_context, + &payer_signer, + &fast_transfer_vaa.vaa_data, + &self.testing_context.get_wormhole_program_id(), + None, + ) + .await + .expect("Failed to create guardian signatures"); let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( &[ @@ -300,9 +297,7 @@ impl TestingEngine { test_context, &payer_signer, fast_market_order, - guardian_set_pubkey, - guardian_signatures_pubkey, - guardian_set_bump, + &guardian_signature_info, config.expected_error.as_ref(), ) .await; @@ -318,8 +313,8 @@ impl TestingEngine { fast_market_order, }, guardian_set_state: GuardianSetState { - guardian_set_address: guardian_set_pubkey, - guardian_signatures_address: guardian_signatures_pubkey, + guardian_set_address: guardian_signature_info.guardian_set_pubkey, + guardian_signatures_address: guardian_signature_info.guardian_signatures_pubkey, }, } } else { @@ -419,7 +414,6 @@ impl TestingEngine { fast_vaa, config.offer_price, &payer_signer, - self.testing_context.get_matching_engine_program_id(), expected_error, ) .await; @@ -456,9 +450,6 @@ impl TestingEngine { .get(config.solver_index) .expect("Solver not found at index"); let offer_price = config.offer_price; - let auction_config_address = current_state - .auction_config_address() - .expect("Auction config address not found"); let payer_signer = config .payer_signer .clone() @@ -466,9 +457,7 @@ impl TestingEngine { let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, test_context, - self.testing_context.get_matching_engine_program_id(), solver.clone(), - auction_config_address, offer_price, &payer_signer, current_state.auction_state(), @@ -811,7 +800,6 @@ impl TestingEngine { current_state.auction_state(), &prepared_order_response, &prepared_custody_token, - &self.testing_context.get_matching_engine_program_id(), config.expected_error.as_ref(), ) .await; diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index b6138b13b..9bd796f42 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -152,12 +152,12 @@ pub async fn add_cctp_router_endpoint_ix( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Keypair, - admin_owner_or_assistant: Pubkey, admin_custodian: Pubkey, admin_keypair: &Keypair, remote_token_messenger: Pubkey, chain: Chain, ) -> TestRouterEndpoint { + let admin_owner_or_assistant = admin_keypair.pubkey(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let program_id = testing_context.get_matching_engine_program_id(); let admin = generate_admin(admin_owner_or_assistant, admin_custodian); @@ -325,7 +325,6 @@ pub async fn create_cctp_router_endpoint( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Keypair, - admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, chain: Chain, @@ -343,7 +342,6 @@ pub async fn create_cctp_router_endpoint( testing_context, test_context, payer_signer, - admin_owner_or_assistant, custodian_address, admin_keypair.as_ref(), remote_token_messenger, @@ -356,7 +354,6 @@ pub async fn create_all_router_endpoints_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Keypair, - admin_owner_or_assistant: Pubkey, custodian_address: Pubkey, admin_keypair: Rc, chains: HashSet, @@ -369,7 +366,7 @@ pub async fn create_all_router_endpoints_test( testing_context, test_context, payer_signer, - admin_owner_or_assistant, + admin_keypair.pubkey(), custodian_address, admin_keypair.as_ref(), ) @@ -381,7 +378,6 @@ pub async fn create_all_router_endpoints_test( testing_context, test_context, payer_signer, - admin_owner_or_assistant, custodian_address, admin_keypair.clone(), chain, diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index d56139150..58330f721 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -269,6 +269,26 @@ impl Default for CreateFastTransferParams { } } +pub struct TestVaaArgs { + pub start_timestamp: Option, + pub sequence: u64, + pub cctp_nonce: u64, + pub vaa_nonce: u32, + pub is_posted: bool, +} + +impl From for TestVaaArgs { + fn from(vaa_args: VaaArgs) -> Self { + Self { + start_timestamp: vaa_args.start_timestamp, + sequence: vaa_args.sequence.unwrap_or_default(), + cctp_nonce: vaa_args.cctp_nonce.unwrap_or_default(), + vaa_nonce: vaa_args.vaa_nonce.unwrap_or_default(), + is_posted: vaa_args.post_vaa, + } + } +} + #[derive(Clone)] pub struct TestVaaPair { pub token_mint: Pubkey, @@ -282,47 +302,47 @@ pub struct TestVaaPair { } impl TestVaaPair { - #[allow(clippy::too_many_arguments)] pub fn new( - start_timestamp: Option, token_mint: Pubkey, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, - cctp_nonce: u64, - vaa_nonce: u32, - sequence: u64, cctp_mint_recipient: Pubkey, create_deposit_and_fast_transfer_params: &CreateDepositAndFastTransferParams, - is_posted: bool, + test_vaa_args: &TestVaaArgs, ) -> Self { create_deposit_and_fast_transfer_params.verify(); let deposit_params = &create_deposit_and_fast_transfer_params.deposit_params; let create_fast_transfer_params = &create_deposit_and_fast_transfer_params.fast_transfer_params; + let start_timestamp = test_vaa_args.start_timestamp; + let sequence = test_vaa_args.sequence; + let cctp_nonce = test_vaa_args.cctp_nonce; + let vaa_nonce = test_vaa_args.vaa_nonce; + let is_posted = test_vaa_args.is_posted; let (deposit_vaa_pubkey, deposit_vaa_data, deposit) = create_deposit_message( token_mint, source_address.clone(), destination_address.clone(), - cctp_nonce, - sequence, cctp_mint_recipient, deposit_params.amount, deposit_params.base_fee, - vaa_nonce, + test_vaa_args, ); + let test_vaa_args = TestVaaArgs { + start_timestamp, + sequence: sequence.saturating_add(1), + cctp_nonce, + vaa_nonce, + is_posted, + }; let (fast_transfer_vaa_pubkey, fast_transfer_vaa_data, fast_market_order) = create_fast_transfer_message( - start_timestamp, source_address.clone(), refund_address.clone(), destination_address.clone(), - vaa_nonce, - sequence.saturating_add(1), - create_fast_transfer_params.amount_in, - create_fast_transfer_params.min_amount_out, - create_fast_transfer_params.max_fee, - create_fast_transfer_params.init_auction_fee, + &test_vaa_args, + create_fast_transfer_params, ); Self { token_mint, @@ -392,19 +412,19 @@ impl TestVaaPair { } } -#[allow(clippy::too_many_arguments)] pub fn create_deposit_message( token_mint: Pubkey, source_address: ChainAddress, _destination_address: ChainAddress, - cctp_nonce: u64, - sequence: u64, cctp_mint_recipient: Pubkey, amount: i32, base_fee: u64, - vaa_nonce: u32, + test_vaa_args: &TestVaaArgs, ) -> (Pubkey, PostedVaaData, Deposit) { let slow_order_response = SlowOrderResponse { base_fee }; + let cctp_nonce = test_vaa_args.cctp_nonce; + let sequence = test_vaa_args.sequence; + let vaa_nonce = test_vaa_args.vaa_nonce; // Implements TypePrefixedPayload let deposit = Deposit { token_address: token_mint.to_bytes(), @@ -434,19 +454,20 @@ pub fn create_deposit_message( (vaa_address, posted_vaa_data, deposit) } -#[allow(clippy::too_many_arguments)] pub fn create_fast_transfer_message( - start_timestamp: Option, source_address: ChainAddress, refund_address: ChainAddress, destination_address: ChainAddress, - vaa_nonce: u32, - sequence: u64, - amount_in: u64, - min_amount_out: u64, - max_fee: u64, - init_auction_fee: u64, + test_vaa_args: &TestVaaArgs, + create_fast_transfer_params: &CreateFastTransferParams, ) -> (Pubkey, PostedVaaData, FastMarketOrder) { + let amount_in = create_fast_transfer_params.amount_in; + let min_amount_out = create_fast_transfer_params.min_amount_out; + let max_fee = create_fast_transfer_params.max_fee; + let init_auction_fee = create_fast_transfer_params.init_auction_fee; + let start_timestamp = test_vaa_args.start_timestamp; + let sequence = test_vaa_args.sequence; + let vaa_nonce = test_vaa_args.vaa_nonce; // If start timestamp is not provided, set the deadline to 0 let deadline = start_timestamp .map(|timestamp| timestamp.saturating_add(10)) @@ -523,18 +544,23 @@ impl TestVaaPairs { let is_posted = vaa_args.post_vaa; let create_deposit_and_fast_transfer_params = &vaa_args.create_deposit_and_fast_transfer_params; + + let test_vaa_args = TestVaaArgs { + start_timestamp: vaa_args.start_timestamp, + sequence, + cctp_nonce, + vaa_nonce, + is_posted, + }; + let test_fast_transfer = TestVaaPair::new( - vaa_args.start_timestamp, token_mint, source_address, refund_address, destination_address, - cctp_nonce, - vaa_nonce, - sequence, cctp_mint_recipient, create_deposit_and_fast_transfer_params, - is_posted, + &test_vaa_args, ); self.0.push(test_fast_transfer); } From 3f08119c5009a1557a8c4a913614a3ffca66cd39 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Sun, 30 Mar 2025 23:51:08 +0100 Subject: [PATCH 048/112] removed clippy refcell ignore --- .../modules/matching-engine-testing/tests/integration_tests.rs | 1 - solana/modules/matching-engine-testing/tests/utils/mod.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs index 3743431d4..31a93d741 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/integration_tests.rs @@ -1,7 +1,6 @@ #![allow(clippy::expect_used)] #![allow(dead_code)] #![allow(clippy::panic)] -#![allow(clippy::await_holding_refcell_ref)] use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; diff --git a/solana/modules/matching-engine-testing/tests/utils/mod.rs b/solana/modules/matching-engine-testing/tests/utils/mod.rs index db70e2aaa..820658ae2 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mod.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mod.rs @@ -1,7 +1,6 @@ #![allow(clippy::expect_used)] #![allow(dead_code)] #![allow(clippy::panic)] -#![allow(clippy::await_holding_refcell_ref)] pub mod account_fixtures; pub mod airdrop; From c5477f0470f202d4a5ac21d33f08cdc84ca2512c Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 1 Apr 2025 13:05:45 +0100 Subject: [PATCH 049/112] more docs --- .../tests/integration_tests.rs | 9 +- .../tests/shimful/shims_execute_order.rs | 2 +- .../shimful/shims_prepare_order_response.rs | 25 +- .../tests/shimless/execute_order.rs | 2 +- .../tests/shimless/prepare_order_response.rs | 24 +- .../tests/testing_engine/config.rs | 72 ++++++ .../tests/testing_engine/engine.rs | 63 ++++-- .../tests/utils/auction.rs | 2 +- .../tests/utils/mod.rs | 2 - .../tests/utils/setup.rs | 139 ++++++++++-- .../tests/utils/token_account.rs | 16 ++ .../tests/utils/tracing.rs | 97 -------- .../tests/utils/vaa.rs | 213 ++++++++++++++++-- 13 files changed, 489 insertions(+), 177 deletions(-) delete mode 100644 solana/modules/matching-engine-testing/tests/utils/tracing.rs diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/integration_tests.rs index 31a93d741..f3bc423c9 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/integration_tests.rs @@ -1,5 +1,4 @@ #![allow(clippy::expect_used)] -#![allow(dead_code)] #![allow(clippy::panic)] use anchor_lang::AccountDeserialize; @@ -108,8 +107,12 @@ pub async fn test_setup_vaas() { post_vaa: true, ..VaaArgs::default() }; - let (testing_context, mut test_context) = - setup_environment(ShimMode::PostVaa, transfer_direction, Some(vaa_args)).await; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifySignature, + transfer_direction, + Some(vaa_args), + ) + .await; testing_context.verify_vaas(&mut test_context).await; diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index cc57b4a38..18728090f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -232,7 +232,7 @@ pub async fn execute_order_fallback_test( active_auction_state, &testing_context.testing_actors.owner.pubkey(), &fixture_accounts, - testing_context.testing_state.transfer_direction, + testing_context.initial_testing_state.transfer_direction, ); execute_order_fallback( testing_context, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index db6f5ec8b..d2a5bf990 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -53,7 +53,7 @@ pub struct PrepareOrderResponseShimAccountsFixture { } impl PrepareOrderResponseShimAccountsFixture { - #[allow(clippy::too_many_arguments)] + #[allow(clippy::too_many_arguments)] // TODO: fix this (10/7) pub fn new( signer: &Pubkey, fixture_accounts: &FixtureAccounts, @@ -304,16 +304,17 @@ pub async fn prepare_order_response_test( .await .unwrap(); - let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; + let source_remote_token_messenger = + match testing_context.initial_testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; let cctp_nonce = deposit.cctp_nonce; let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; @@ -366,7 +367,7 @@ pub async fn prepare_order_response_test( usdc_mint_address, &cctp_message_decoded, &guardian_signature_info, - &testing_context.testing_state.transfer_direction, + &testing_context.initial_testing_state.transfer_direction, ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( testing_context, diff --git a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs index 22961c21f..fe1be2965 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs @@ -106,7 +106,7 @@ pub fn create_execute_order_shimless_accounts( Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; let token_messenger = Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let remote_token_messenger = match testing_context.testing_state.transfer_direction { + let remote_token_messenger = match testing_context.initial_testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.arbitrum_remote_token_messenger } diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 8bba7e58d..9044bea76 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -50,16 +50,17 @@ pub async fn prepare_order_response( .clone() .expect("Fixture accounts not found"); - let source_remote_token_messenger = match testing_context.testing_state.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; + let source_remote_token_messenger = + match testing_context.initial_testing_state.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; let custodian_address = testing_engine_state @@ -144,7 +145,8 @@ pub async fn prepare_order_response( }; let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let cctp_remote_token_messenger = match testing_context.testing_state.transfer_direction { + let cctp_remote_token_messenger = match testing_context.initial_testing_state.transfer_direction + { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.ethereum_remote_token_messenger } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 146cd65f1..c2b4975bb 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -4,8 +4,20 @@ use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; use anchor_lang::prelude::*; use solana_sdk::signature::Keypair; +pub trait InstructionConfig: Default { + fn expected_error(&self) -> Option<&ExpectedError>; +} + +/// A type alias for an optional value that overwrites the current state pub type OverwriteCurrentState = Option; +/// A struct representing an expected error +/// +/// # Fields +/// +/// * `instruction_index` - The index of the instruction that is expected to error +/// * `error_code` - The error code that is expected to be returned +/// * `error_string` - The error string that is expected to be returned #[derive(Clone)] pub struct ExpectedError { pub instruction_index: u8, @@ -13,6 +25,12 @@ pub struct ExpectedError { pub error_string: String, } +/// A struct representing an expected log +/// +/// # Fields +/// +/// * `log_message` - The log message that is expected to be returned +/// * `count` - The number of times the log message is expected to appear #[derive(Clone)] pub struct ExpectedLog { pub log_message: String, @@ -25,6 +43,11 @@ pub struct InitializeInstructionConfig { pub expected_error: Option, } +impl InstructionConfig for InitializeInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} pub struct CreateCctpRouterEndpointsInstructionConfig { pub chains: HashSet, pub payer_signer: Option>, @@ -42,6 +65,13 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { } } } + +impl InstructionConfig for CreateCctpRouterEndpointsInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + #[derive(Clone, Default)] pub struct InitializeFastMarketOrderShimInstructionConfig { pub fast_market_order_id: u32, @@ -50,6 +80,12 @@ pub struct InitializeFastMarketOrderShimInstructionConfig { pub expected_error: Option, } +impl InstructionConfig for InitializeFastMarketOrderShimInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + #[derive(Clone, Default)] pub struct PrepareOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, @@ -59,6 +95,12 @@ pub struct PrepareOrderInstructionConfig { pub expected_log_messages: Option>, } +impl InstructionConfig for PrepareOrderInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + #[derive(Clone, Default)] pub struct ExecuteOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, @@ -67,12 +109,24 @@ pub struct ExecuteOrderInstructionConfig { pub expected_error: Option, } +impl InstructionConfig for ExecuteOrderInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + #[derive(Clone, Default)] pub struct SettleAuctionInstructionConfig { pub payer_signer: Option>, pub expected_error: Option, } +impl InstructionConfig for SettleAuctionInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + #[derive(Clone, Default)] pub struct CloseFastMarketOrderShimInstructionConfig { pub close_account_refund_recipient_keypair: Option>, // If none, will use the solver 0 keypair @@ -80,6 +134,12 @@ pub struct CloseFastMarketOrderShimInstructionConfig { pub expected_error: Option, } +impl InstructionConfig for CloseFastMarketOrderShimInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + pub struct PlaceInitialOfferInstructionConfig { pub solver_index: usize, pub offer_price: u64, @@ -100,6 +160,12 @@ impl Default for PlaceInitialOfferInstructionConfig { } } +impl InstructionConfig for PlaceInitialOfferInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} + pub struct ImproveOfferInstructionConfig { pub solver_index: usize, pub offer_price: u64, @@ -117,3 +183,9 @@ impl Default for ImproveOfferInstructionConfig { } } } + +impl InstructionConfig for ImproveOfferInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 3d2f98105..0259c8d91 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -36,6 +36,33 @@ pub enum InstructionTrigger { CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } +// Implement InstructionConfig for InstructionTrigger +impl InstructionConfig for InstructionTrigger { + fn expected_error(&self) -> Option<&ExpectedError> { + match self { + Self::InitializeProgram(config) => config.expected_error(), + Self::CreateCctpRouterEndpoints(config) => config.expected_error(), + Self::InitializeFastMarketOrderShim(config) => config.expected_error(), + Self::PlaceInitialOfferShimless(config) => config.expected_error(), + Self::PlaceInitialOfferShim(config) => config.expected_error(), + Self::ImproveOfferShimless(config) => config.expected_error(), + Self::ExecuteOrderShimless(config) => config.expected_error(), + Self::ExecuteOrderShim(config) => config.expected_error(), + Self::PrepareOrderShimless(config) => config.expected_error(), + Self::PrepareOrderShim(config) => config.expected_error(), + Self::SettleAuction(config) => config.expected_error(), + Self::CloseFastMarketOrderShim(config) => config.expected_error(), + } + } +} + +// If you need a default implementation +impl Default for InstructionTrigger { + fn default() -> Self { + Self::InitializeProgram(InitializeInstructionConfig::default()) + } +} + /// Functional style testing engine for the matching engine program /// /// This engine is used to test the matching engine program with a functional style. @@ -46,6 +73,7 @@ pub enum InstructionTrigger { /// If an instruction trigger fails, the engine will return the previous state. /// /// Instruction triggers (enums) take a configuration struct as an argument. +/// Each instruction config implements the InstructionConfig trait. /// The configuration struct contains fields for the expected error, and for /// providing test specific configuration. /// @@ -159,8 +187,11 @@ impl TestingEngine { .fixture_accounts .clone() .expect("Failed to get fixture accounts"); - let vaas: TestVaaPairs = self.testing_context.testing_state.vaas.clone(); - let transfer_direction = self.testing_context.testing_state.transfer_direction; + let vaas: TestVaaPairs = self.testing_context.initial_testing_state.vaas.clone(); + let transfer_direction = self + .testing_context + .initial_testing_state + .transfer_direction; TestingEngineState::Uninitialized(BaseState { fixture_accounts, vaas, @@ -175,7 +206,7 @@ impl TestingEngine { config: &InitializeInstructionConfig, ) -> TestingEngineState { let auction_parameters_config = config.auction_parameters_config.clone(); - let expected_error = config.expected_error.as_ref(); + let expected_error = config.expected_error(); let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { let result = shimless::initialize::initialize_program( @@ -298,7 +329,7 @@ impl TestingEngine { &payer_signer, fast_market_order, &guardian_signature_info, - config.expected_error.as_ref(), + config.expected_error(), ) .await; @@ -345,7 +376,7 @@ impl TestingEngine { test_context, &close_account_refund_recipient, &fast_market_order_account, - config.expected_error.as_ref(), + config.expected_error(), ) .await; @@ -379,7 +410,7 @@ impl TestingEngine { .solvers .get(config.solver_index) .expect("Solver not found at index"); - let expected_error = config.expected_error.as_ref(); + let expected_error = config.expected_error(); let fast_vaa = ¤t_state .base() .vaas @@ -405,7 +436,7 @@ impl TestingEngine { .endpoints, custodian_address, self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, + current_state.base().transfer_direction, ); let auction_state = shimless::make_offer::place_initial_offer_shimless( &self.testing_context, @@ -442,7 +473,7 @@ impl TestingEngine { current_state: &TestingEngineState, config: &ImproveOfferInstructionConfig, ) -> TestingEngineState { - let expected_error = config.expected_error.as_ref(); + let expected_error = config.expected_error(); let solver = self .testing_context .testing_actors @@ -511,7 +542,7 @@ impl TestingEngine { &router_endpoints.endpoints, custodian_address, self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, + current_state.base().transfer_direction, ); let fast_vaa_data = current_state .get_first_test_vaa_pair() @@ -527,7 +558,7 @@ impl TestingEngine { &fast_market_order_address, &auction_accounts, config.offer_price, - config.expected_error.as_ref(), + config.expected_error(), ) .await; if config.expected_error.is_none() { @@ -581,7 +612,7 @@ impl TestingEngine { &fast_market_order_address, active_auction_state, solver, - config.expected_error.as_ref(), + config.expected_error(), ) .await; if config.expected_error.is_none() { @@ -637,7 +668,7 @@ impl TestingEngine { &router_endpoints.endpoints, custodian_address, self.testing_context.get_usdc_mint_address(), - self.testing_context.testing_state.transfer_direction, + current_state.base().transfer_direction, ); let result = shimless::execute_order::execute_order_shimless_test( &self.testing_context, @@ -645,7 +676,7 @@ impl TestingEngine { &auction_accounts, current_state.auction_state(), &payer_signer, - config.expected_error.as_ref(), + config.expected_error(), ) .await; if config.expected_error.is_none() { @@ -702,7 +733,7 @@ impl TestingEngine { &auction_accounts.to_router_endpoint, &auction_accounts.from_router_endpoint, &deposit, - config.expected_error.as_ref(), + config.expected_error(), ) .await; if config.expected_error.is_none() { @@ -754,7 +785,7 @@ impl TestingEngine { &auction_accounts.to_router_endpoint, &auction_accounts.from_router_endpoint, &solver_token_account, - config.expected_error.as_ref(), + config.expected_error(), config.expected_log_messages.as_ref(), ) .await; @@ -800,7 +831,7 @@ impl TestingEngine { current_state.auction_state(), &prepared_order_response, &prepared_custody_token, - config.expected_error.as_ref(), + config.expected_error(), ) .await; match auction_state { diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 03c5bb7f9..87294bbc7 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -111,7 +111,7 @@ impl ActiveAuctionState { custody_token_bump: 254, // TODO: Figure this out vaa_sequence: 0, // No need to cehck against this source_chain: { - match testing_context.testing_state.transfer_direction { + match testing_context.initial_testing_state.transfer_direction { TransferDirection::FromEthereumToArbitrum => 3, TransferDirection::FromArbitrumToEthereum => 23, TransferDirection::Other => { diff --git a/solana/modules/matching-engine-testing/tests/utils/mod.rs b/solana/modules/matching-engine-testing/tests/utils/mod.rs index 820658ae2..a3d6a3cd7 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mod.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mod.rs @@ -1,5 +1,4 @@ #![allow(clippy::expect_used)] -#![allow(dead_code)] #![allow(clippy::panic)] pub mod account_fixtures; @@ -12,6 +11,5 @@ pub mod program_fixtures; pub mod router; pub mod setup; pub mod token_account; -// pub mod tracing; pub mod vaa; pub use constants::*; diff --git a/solana/modules/matching-engine-testing/tests/utils/setup.rs b/solana/modules/matching-engine-testing/tests/utils/setup.rs index ee3e50f77..2da1c10c3 100644 --- a/solana/modules/matching-engine-testing/tests/utils/setup.rs +++ b/solana/modules/matching-engine-testing/tests/utils/setup.rs @@ -54,6 +54,14 @@ cfg_if::cfg_if! { } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; +/// The pre-testing context struct stores data that for the program before the solana-program-test context is created +/// +/// # Fields +/// +/// * `program_test` - The program test +/// * `testing_actors` - The testing actors +/// * `program_data_pubkey` - The pubkey of the program data account +/// * `account_fixtures` - The account fixtures pub struct PreTestingContext { pub program_test: ProgramTest, pub testing_actors: TestingActors, @@ -112,23 +120,44 @@ impl PreTestingContext { } } + /// Adds the post message shims to the program test pub fn add_post_message_shims(&mut self) { initialise_post_message_shims(&mut self.program_test); } + /// Adds the verify shims to the program test pub fn add_verify_shims(&mut self) { initialise_verify_shims(&mut self.program_test); } } +/// Testing Context struct that stores common data needed to run tests +/// +/// # Fields +/// +/// * `program_data_account` - The pubkey of the program data account created by the Upgrade Manager +/// * `testing_actors` - The testing actors, including solvers and the owner +/// * `fixture_accounts` - The accounts that are loaded from files under the `tests/fixtures` directory +/// * `testing_state` - The testing state, including the auction state and the Vaas pub struct TestingContext { pub program_data_account: Pubkey, pub testing_actors: TestingActors, pub fixture_accounts: Option, - pub testing_state: TestingState, + pub initial_testing_state: TestingState, } impl TestingContext { + /// Creates a new TestingContext + /// + /// # Arguments + /// + /// * `pre_testing_context` - The pre-testing context + /// * `transfer_direction` - The transfer direction + /// * `vaas_test` - The Vaas that were created in the pre-testing context setup stage + /// + /// # Returns + /// + /// A tuple containing the new TestingContext and the test context from the solana-program-test crate pub async fn new( mut pre_testing_context: PreTestingContext, transfer_direction: TransferDirection, @@ -150,7 +179,7 @@ impl TestingContext { .testing_actors .create_atas(&mut test_context, USDC_MINT_ADDRESS) .await; - let testing_state = match vaas_test { + let initial_testing_state = match vaas_test { Some(vaas_test) => TestingState { vaas: vaas_test, transfer_direction, @@ -166,47 +195,87 @@ impl TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, fixture_accounts: Some(pre_testing_context.account_fixtures), - testing_state, + initial_testing_state, }, test_context, ) } + /// Verifies the posted VAA pairs + /// + /// # Arguments + /// + /// * `test_context` - The test context pub async fn verify_vaas(&self, test_context: &mut ProgramTestContext) { - self.testing_state + self.initial_testing_state .vaas .verify_posted_vaas(test_context) .await; } + /// Gets the VAA pair at the given index + /// + /// # Arguments + /// + /// * `index` - The index of the VAA pair pub fn get_vaa_pair(&self, index: usize) -> Option { - if index < self.testing_state.vaas.len() { - Some(self.testing_state.vaas[index].clone()) + if index < self.initial_testing_state.vaas.len() { + Some(self.initial_testing_state.vaas[index].clone()) } else { None } } + /// Gets the fixture accounts + /// + /// # Returns + /// + /// The fixture accounts pub fn get_fixture_accounts(&self) -> Option { self.fixture_accounts.clone() } + /// Gets the matching engine program ID + /// + /// # Returns + /// + /// The matching engine program ID pub fn get_matching_engine_program_id(&self) -> Pubkey { PROGRAM_ID } + /// Gets the USDC mint address + /// + /// # Returns + /// + /// The USDC mint address pub fn get_usdc_mint_address(&self) -> Pubkey { USDC_MINT_ADDRESS } + /// Gets the CCTP mint recipient + /// + /// # Returns + /// + /// The CCTP mint recipient pub fn get_cctp_mint_recipient(&self) -> Pubkey { CCTP_MINT_RECIPIENT } + /// Gets the Wormhole program ID + /// + /// # Returns + /// + /// The Wormhole program ID pub fn get_wormhole_program_id(&self) -> Pubkey { wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID } + /// Gets the new latest blockhash + /// + /// # Arguments + /// + /// * `test_context` - The test context pub async fn get_new_latest_blockhash( &self, test_context: &mut ProgramTestContext, @@ -216,6 +285,12 @@ impl TestingContext { Ok(hash) } + /// Processes a transaction + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `transaction` - The transaction to process pub async fn process_transaction( &self, test_context: &mut ProgramTestContext, @@ -236,6 +311,7 @@ impl TestingContext { /// /// The simulation details if the transaction was successful and all expected logs were found #[allow(dead_code)] + // TODO: Use this pub async fn simulate_and_verify_logs( &self, test_context: &mut ProgramTestContext, @@ -323,6 +399,11 @@ impl TestingContext { } } +/// A struct representing a solver +/// +/// # Fields +/// +/// * `actor` - The testing actor #[derive(Clone)] pub struct Solver { pub actor: TestingActor, @@ -385,6 +466,12 @@ impl Solver { } } +/// A struct representing a testing actor +/// +/// # Fields +/// +/// * `keypair` - The keypair of the actor +/// * `token_account` - The token account of the actor (if it exists) #[derive(Clone)] pub struct TestingActor { pub keypair: Rc, @@ -420,6 +507,11 @@ impl TestingActor { self.token_account.as_ref().map(|t| t.address) } + /// Gets the balance of the token account + /// + /// # Arguments + /// + /// * `test_context` - The test context pub async fn get_balance(&self, test_context: &mut ProgramTestContext) -> u64 { if let Some(token_account) = self.token_account_address() { let account = test_context @@ -525,6 +617,12 @@ impl TestingActors { } } +/// Fast forwards the slot in the test context +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `num_slots` - The number of slots to fast forward pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots: u64) { // Get the current slot let mut current_slot = test_context.banks_client.get_root_slot().await.unwrap(); @@ -557,11 +655,12 @@ pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots println!("Fast forwarded {} slots", num_slots); } -#[derive(Clone)] -pub struct ProgramAddresses { - pub custodian_address: Pubkey, -} - +/// A struct representing the testing state +/// +/// # Fields +/// +/// * `auction_state` - The auction state +/// * `vaas` - The VAAs pub struct TestingState { pub auction_state: AuctionState, pub vaas: TestVaaPairs, @@ -578,10 +677,17 @@ impl Default for TestingState { } } +/// The mode of the shim +/// +/// # Enums +/// +/// * `None` - No shims +/// * `PostVaa` - Post the VAAs but don't add the shims +/// * `VerifySignature` - Only add the verify signature shim program +/// * `VerifyAndPostSignature` - Add the verify signature and post message shims program pub enum ShimMode { None, - PostVaa, - // VerifySignature, + VerifySignature, VerifyAndPostSignature, } @@ -670,10 +776,9 @@ pub async fn setup_environment( }; match shim_mode { ShimMode::None => {} - ShimMode::PostVaa => {} - // ShimMode::VerifySignature => { - // pre_testing_context.add_verify_shims(); - // } + ShimMode::VerifySignature => { + pre_testing_context.add_verify_shims(); + } ShimMode::VerifyAndPostSignature => { pre_testing_context.add_verify_shims(); pre_testing_context.add_post_message_shims(); diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index 8e1ac5da7..4c05b4d1d 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -8,6 +8,7 @@ use solana_sdk::{ use std::fs; #[derive(Clone)] +/// A struct representing an initialised token account pub struct TokenAccountFixture { pub address: Pubkey, pub account: spl_token::state::Account, @@ -31,6 +32,10 @@ impl std::fmt::Debug for TokenAccountFixture { /// * `payer` - The payer of the account /// * `owner` - The owner of the account /// * `mint` - The mint of the account +/// +/// # Returns +/// +/// The token account fixture pub async fn create_token_account( test_ctx: &mut ProgramTestContext, owner: &Keypair, @@ -75,6 +80,17 @@ pub async fn create_token_account( } } +/// Creates a token account for the given PDA +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `pda` - The PDA that will own the token account +/// * `mint` - The mint address of the token +/// +/// # Returns +/// +/// The address of the token account pub async fn create_token_account_for_pda( test_context: &mut ProgramTestContext, pda: &Pubkey, // The PDA that will own the token account diff --git a/solana/modules/matching-engine-testing/tests/utils/tracing.rs b/solana/modules/matching-engine-testing/tests/utils/tracing.rs deleted file mode 100644 index d69bfa754..000000000 --- a/solana/modules/matching-engine-testing/tests/utils/tracing.rs +++ /dev/null @@ -1,97 +0,0 @@ -use once_cell::sync::Lazy; -use std::sync::{Arc, Mutex}; -use tracing::subscriber::set_global_default; -use tracing_log::LogTracer; -use tracing_subscriber::{ - fmt::{self, format::FmtSpan}, - layer::SubscriberExt, - EnvFilter, Registry, -}; - -// Global storage for captured logs -static CAPTURED_LOGS: Lazy>>> = - Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); - -// Initialize the tracing subscriber -pub fn init_tracing() { - // Only initialize once - static INITIALIZED: Lazy = Lazy::new(|| { - // Create a custom layer that captures logs - let fmt_layer = fmt::Layer::default() - .with_span_events(FmtSpan::CLOSE) - .with_writer(move || LogCaptureWriter::new(Arc::clone(&CAPTURED_LOGS))); - - // Set up the subscriber with an environment filter - let subscriber = Registry::default() - .with( - EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("solana=debug")), - ) - .with(fmt_layer); - - // Set the subscriber as the global default - set_global_default(subscriber).expect("Failed to set tracing subscriber"); - - // Bridge from log to tracing - LogTracer::init().expect("Failed to initialize log tracer"); - - true - }); - - let _ = *INITIALIZED; -} - -// Clear captured logs -pub fn clear_logs() { - let mut logs = CAPTURED_LOGS.lock().unwrap(); - logs.clear(); -} - -// Get captured logs -pub fn get_logs() -> Vec { - let logs = CAPTURED_LOGS.lock().unwrap(); - logs.clone() -} - -// Get logs containing a specific string -pub fn get_logs_containing(pattern: &str) -> Vec { - let logs = CAPTURED_LOGS.lock().unwrap(); - logs.iter() - .filter(|log| log.contains(pattern)) - .cloned() - .collect() -} - -// Check if logs contain a specific string -pub fn logs_contain(pattern: &str) -> bool { - let logs = CAPTURED_LOGS.lock().unwrap(); - logs.iter().any(|log| log.contains(pattern)) -} - -// Custom writer that captures logs -struct LogCaptureWriter { - logs: Arc>>, -} - -impl LogCaptureWriter { - fn new(logs: Arc>>) -> Self { - Self { logs } - } -} - -impl std::io::Write for LogCaptureWriter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - if let Ok(log) = std::str::from_utf8(buf) { - let log = log.trim().to_string(); - if !log.is_empty() { - let mut logs = self.logs.lock().unwrap(); - logs.push(log); - } - } - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index 58330f721..3ecd56265 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -20,38 +20,41 @@ pub trait DataDiscriminator { const DISCRIMINATOR: &'static [u8]; } +/// A struct representing a posted VAA +/// +/// # Fields +/// +/// * `consistency_level` - The level of consistency requested by the emitter +/// * `vaa_time` - The time the VAA was submitted +/// * `vaa_signature_account` - The account where signatures are stored +/// * `submission_time` - The time the posted message was created +/// * `nonce` - The unique nonce for this message +/// * `sequence` - The sequence number of this message +/// * `emitter_chain` - The chain ID of the emitter +/// * `emitter_address` - The address of the emitter +/// * `payload` - The payload of the VAA #[derive( Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq, Eq, )] pub struct PostedVaaData { /// Header of the posted VAA // pub vaa_version: u8, (This is removed because it is encoded in the discriminator) - - /// Level of consistency requested by the emitter pub consistency_level: u8, - /// Time the vaa was submitted pub vaa_time: u32, - /// Account where signatures are stored pub vaa_signature_account: Pubkey, - /// Time the posted message was created pub submission_time: u32, - /// Unique nonce for this message pub nonce: u32, - /// Sequence number of this message pub sequence: u64, - /// Emitter of the message pub emitter_chain: u16, - /// Emitter of the message pub emitter_address: [u8; 32], - /// Message payload pub payload: Vec, } @@ -60,6 +63,15 @@ impl DataDiscriminator for PostedVaaData { } impl PostedVaaData { + /// Creates a new posted VAA + /// + /// # Arguments + /// + /// * `chain` - The chain the VAA is being posted to + /// * `payload` - The payload of the VAA + /// * `emitter_address` - The address of the emitter + /// * `sequence` - The sequence number of the VAA + /// * `nonce` - The nonce of the VAA pub fn new( chain: Chain, payload: Vec, @@ -88,6 +100,7 @@ impl PostedVaaData { } } + /// Computes the hash of the VAA (needed for the digest of the VAA) pub fn message_hash(&self) -> keccak::Hash { keccak::hashv(&[ self.vaa_time.to_be_bytes().as_ref(), @@ -100,6 +113,16 @@ impl PostedVaaData { ]) } + /// Signs the VAA with the guardian key + /// + /// # Arguments + /// + /// * `guardian_secret_key` - The guardian key + /// * `index` - The index of the guardian + /// + /// # Returns + /// + /// The 66 byte signature (with recovery id at final index and guardian index at first index) pub fn sign_with_guardian_key( &self, guardian_secret_key: &SecpSecretKey, @@ -120,6 +143,11 @@ impl PostedVaaData { signature_bytes } + /// Computes the digest of the VAA + /// + /// # Returns + /// + /// The 32 byte digest of the VAA pub fn digest(&self) -> [u8; 32] { keccak::hashv(&[self.message_hash().as_ref()]) .as_ref() @@ -127,6 +155,12 @@ impl PostedVaaData { .unwrap() } + /// Creates a VAA account + /// + /// # Arguments + /// + /// * `program_test` - The program test + /// * `vaa_address` - The address of the VAA pub fn create_vaa_account(&self, program_test: &mut ProgramTest, vaa_address: Pubkey) { let vaa_data_serialized = serialize_with_discriminator(self).unwrap(); let lamports = solana_sdk::rent::Rent::default().minimum_balance(vaa_data_serialized.len()); @@ -167,6 +201,12 @@ where Ok(data) } +/// A struct representing the deserialized payload of a VAA +/// +/// # Enums +/// +/// * `deposit` - The deposit payload +/// * `fast_transfer` - The fast transfer payload #[derive(Clone)] pub enum PayloadDeserialized { Deposit(Deposit), @@ -181,14 +221,23 @@ impl PayloadDeserialized { } } - pub fn get_fast_transfer(&self) -> Option { - match self { - Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), - _ => None, - } - } + // pub fn get_fast_transfer(&self) -> Option { + // match self { + // Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), + // _ => None, + // } + // } } +/// A struct representing a test VAA (may be posted or not) +/// +/// # Fields +/// +/// * `kind` - The kind of VAA +/// * `vaa_pubkey` - The pubkey of the VAA +/// * `vaa_data` - The data of the VAA +/// * `payload_deserialized` - The deserialized payload of the VAA +/// * `is_posted` - Whether the VAA has been posted #[derive(Clone)] pub struct TestVaa { pub kind: TestVaaKind, @@ -199,10 +248,12 @@ pub struct TestVaa { } impl TestVaa { + /// Gets the pubkey of the VAA pub fn get_vaa_pubkey(&self) -> Pubkey { self.vaa_pubkey } + /// Gets the posted vaa data of the VAA pub fn get_vaa_data(&self) -> &PostedVaaData { &self.vaa_data } @@ -214,6 +265,7 @@ pub enum TestVaaKind { FastTransfer, } +/// A struct representing the parameters for creating a deposit and fast transfer #[derive(Default)] pub struct CreateDepositAndFastTransferParams { pub deposit_params: CreateDepositParams, @@ -221,6 +273,7 @@ pub struct CreateDepositAndFastTransferParams { } impl CreateDepositAndFastTransferParams { + /// Verifies the parameters for creating a deposit and fast transfer pub fn verify(&self) { assert!( self.fast_transfer_params.max_fee @@ -269,6 +322,7 @@ impl Default for CreateFastTransferParams { } } +/// Helper struct for creating test VAA arguments pub struct TestVaaArgs { pub start_timestamp: Option, pub sequence: u64, @@ -289,6 +343,18 @@ impl From for TestVaaArgs { } } +/// A struct representing a pair of test VAA +/// +/// # Fields +/// +/// * `token_mint` - The mint of the token +/// * `source_address` - The source address +/// * `refund_address` - The refund address +/// * `destination_address` - The destination address +/// * `cctp_nonce` - The CCTP nonce +/// * `sequence` - The sequence number +/// * `fast_transfer_vaa` - The fast transfer VAA +/// * `deposit_vaa` - The deposit VAA #[derive(Clone)] pub struct TestVaaPair { pub token_mint: Pubkey, @@ -302,6 +368,17 @@ pub struct TestVaaPair { } impl TestVaaPair { + /// Creates a new test VAA pair + /// + /// # Arguments + /// + /// * `token_mint` - The mint of the token + /// * `source_address` - The source address + /// * `refund_address` - The refund address + /// * `destination_address` - The destination address + /// * `cctp_mint_recipient` - The CCTP mint recipient + /// * `create_deposit_and_fast_transfer_params` - The parameters for creating a deposit and fast transfer + /// * `test_vaa_args` - The arguments for the test VAA pub fn new( token_mint: Pubkey, source_address: ChainAddress, @@ -368,6 +445,11 @@ impl TestVaaPair { } } + /// Adds the VAA pair to the test context + /// + /// # Arguments + /// + /// * `program_test` - The program test pub fn add_to_test(&self, program_test: &mut ProgramTest) { self.deposit_vaa .vaa_data @@ -377,6 +459,7 @@ impl TestVaaPair { .create_vaa_account(program_test, self.fast_transfer_vaa.vaa_pubkey); } + /// Verifies the posted VAA pair pub async fn verify_posted_vaa_pair(&self, test_context: &mut ProgramTestContext) { let expected_deposit_vaa = self.deposit_vaa.vaa_data.clone(); let expected_fast_transfer_vaa = self.fast_transfer_vaa.vaa_data.clone(); @@ -407,11 +490,29 @@ impl TestVaaPair { } } + /// Checks if the VAA pair is posted pub fn is_posted(&self) -> bool { self.deposit_vaa.is_posted && self.fast_transfer_vaa.is_posted } } +/// Creates a deposit message +/// +/// # Arguments +/// +/// * `token_mint` - The mint of the token +/// * `source_address` - The source address +/// * `destination_address` - The destination address (always set to solana regardless of the destination chain) +/// * `cctp_mint_recipient` - The CCTP mint recipient +/// * `amount` - The amount of the deposit +/// * `base_fee` - The base fee of the deposit +/// * `test_vaa_args` - The arguments for the test VAA +/// +/// # Returns +/// +/// * `vaa_address` - The address of the VAA +/// * `posted_vaa_data` - The posted VAA data +/// * `deposit` - The deposit account deserialized pub fn create_deposit_message( token_mint: Pubkey, source_address: ChainAddress, @@ -454,6 +555,21 @@ pub fn create_deposit_message( (vaa_address, posted_vaa_data, deposit) } +/// Creates a fast transfer message +/// +/// # Arguments +/// +/// * `source_address` - The source address +/// * `refund_address` - The refund address +/// * `destination_address` - The destination address +/// * `test_vaa_args` - The arguments for the test VAA +/// * `create_fast_transfer_params` - The parameters for creating a fast transfer +/// +/// # Returns +/// +/// * `vaa_address` - The address of the VAA +/// * `posted_vaa_data` - The posted VAA data +/// * `fast_market_order` - The fast market order account deserialized pub fn create_fast_transfer_message( source_address: ChainAddress, refund_address: ChainAddress, @@ -502,6 +618,11 @@ pub fn create_fast_transfer_message( (vaa_address, posted_vaa_data, fast_market_order) } +/// A struct representing a collection of test VAA pairs +/// +/// # Fields +/// +/// * `pairs` - The collection of test VAA pairs #[derive(Clone)] pub struct TestVaaPairs(pub Vec); @@ -525,6 +646,15 @@ impl TestVaaPairs { } /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer + /// + /// # Arguments + /// + /// * `token_mint` - The mint of the token + /// * `source_address` - The source address + /// * `refund_address` - The refund address + /// * `destination_address` - The destination address + /// * `cctp_mint_recipient` - The CCTP mint recipient + /// * `vaa_args` - The arguments for the test VAA pub fn add_ft( &mut self, token_mint: Pubkey, @@ -565,6 +695,16 @@ impl TestVaaPairs { self.0.push(test_fast_transfer); } + /// Creates a collection of test VAA pairs with a chain and address + /// + /// # Arguments + /// + /// * `program_test` - The program test + /// * `mint_address` - The mint address + /// * `cctp_mint_recipient` - The CCTP mint recipient + /// * `source_chain_and_address` - The source chain and address + /// * `destination_chain_and_address` - The destination chain and address + /// * `vaa_args` - The arguments for the test VAA pub fn create_vaas_with_chain_and_address( &mut self, program_test: &mut ProgramTest, @@ -607,6 +747,16 @@ impl TestVaaPairs { } } +/// A struct representing the arguments for creating a test VAA +/// +/// # Fields +/// +/// * `sequence` - The sequence number +/// * `cctp_nonce` - The CCTP nonce +/// * `vaa_nonce` - The VAA nonce +/// * `start_timestamp` - The start timestamp +/// * `post_vaa` - Whether to post the VAA +/// * `create_deposit_and_fast_transfer_params` - The parameters for creating a deposit and fast transfer #[derive(Default)] pub struct VaaArgs { pub sequence: Option, @@ -622,6 +772,20 @@ pub struct ChainAndAddress { pub address: [u8; 32], } +/// Creates a collection of test VAA pairs with a chain and address (one deposit and one fast transfer per chain) +/// +/// # Arguments +/// +/// * `program_test` - The program test +/// * `mint_address` - The mint address +/// * `cctp_mint_recipient` - The CCTP mint recipient +/// * `source_chain_and_address` - The source chain and address +/// * `destination_chain_and_address` - The destination chain and address +/// * `vaa_args` - The arguments for the test VAA +/// +/// # Returns +/// +/// * `test_vaa_pairs` - The collection of test VAA pairs pub fn create_vaas_test_with_chain_and_address( program_test: &mut ProgramTest, mint_address: Pubkey, @@ -645,6 +809,17 @@ pub trait ToBytes { fn to_bytes(&self) -> [u8; 32]; } +/// A struct representing a test public key +/// +/// # Enums +/// +/// * `solana` - A Solana public key +/// * `evm` - An EVM public key +/// * `bytes` - A bytes representation of the public key +/// +/// # Methods +/// +/// * `to_bytes` - Converts the public key to a bytes array #[allow(dead_code)] #[derive(Debug, Clone)] pub enum TestPubkey { @@ -713,6 +888,12 @@ impl ToBytes for EvmAddress { } } +/// A struct representing a chain and address +/// +/// # Fields +/// +/// * `chain` - The chain +/// * `address` - The address #[derive(Clone)] pub struct ChainAddress { pub chain: Chain, From 1b6d6d80f99646dca9b59880cb003104f7f07ea3 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 1 Apr 2025 13:48:54 +0100 Subject: [PATCH 050/112] refactor to make prettier --- .../matching-engine-testing/tests/mod.rs | 9 ++ .../tests/shimful/fast_market_order_shim.rs | 6 +- .../tests/shimful/post_message.rs | 3 +- .../tests/shimful/shims_execute_order.rs | 9 +- .../tests/shimful/shims_make_offer.rs | 3 +- .../shimful/shims_prepare_order_response.rs | 25 ++- .../tests/shimful/verify_shim.rs | 3 +- .../tests/shimless/execute_order.rs | 6 +- .../tests/shimless/initialize.rs | 2 +- .../tests/shimless/make_offer.rs | 2 +- .../tests/shimless/prepare_order_response.rs | 28 ++-- .../tests/shimless/settle_auction.rs | 2 +- .../happy_path.rs} | 31 ++-- .../tests/test_scenarios/mod.rs | 1 + .../tests/testing_engine/config.rs | 1 + .../tests/testing_engine/engine.rs | 49 +++++- .../tests/testing_engine/mod.rs | 1 + .../tests/{utils => testing_engine}/setup.rs | 107 +++---------- .../tests/testing_engine/state.rs | 2 +- .../tests/utils/auction.rs | 4 +- .../tests/utils/mod.rs | 2 +- .../tests/utils/public_keys.rs | 142 ++++++++++++++++++ .../tests/utils/router.rs | 2 +- .../tests/utils/vaa.rs | 139 +---------------- 24 files changed, 281 insertions(+), 298 deletions(-) create mode 100644 solana/modules/matching-engine-testing/tests/mod.rs rename solana/modules/matching-engine-testing/tests/{integration_tests.rs => test_scenarios/happy_path.rs} (98%) create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs rename solana/modules/matching-engine-testing/tests/{utils => testing_engine}/setup.rs (89%) create mode 100644 solana/modules/matching-engine-testing/tests/utils/public_keys.rs diff --git a/solana/modules/matching-engine-testing/tests/mod.rs b/solana/modules/matching-engine-testing/tests/mod.rs new file mode 100644 index 000000000..301013a66 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/mod.rs @@ -0,0 +1,9 @@ +#![allow(clippy::expect_used)] +#![allow(clippy::panic)] + +mod shimful; +mod shimless; +// Where tests are located +mod test_scenarios; +mod testing_engine; +mod utils; diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index e5d9c34c4..f5333de20 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -1,9 +1,8 @@ use crate::testing_engine::config::ExpectedError; -use super::super::utils; -use super::super::utils::constants::*; -use super::super::utils::setup::TestingContext; use super::verify_shim::GuardianSignatureInfo; +use crate::testing_engine::setup::TestingContext; +use crate::utils; use common::messages::FastMarketOrder; use matching_engine::fallback::close_fast_market_order::{ CloseFastMarketOrder as CloseFastMarketOrderFallback, @@ -14,6 +13,7 @@ use matching_engine::fallback::initialise_fast_market_order::{ InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, }; +use utils::constants::*; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, FastMarketOrderParams}; use solana_program_test::ProgramTestContext; diff --git a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs index aa34af823..f7747696f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs @@ -1,4 +1,5 @@ -use crate::utils::{constants::*, setup::TestingContext}; +use crate::testing_engine::setup::TestingContext; +use crate::utils::constants::*; use solana_program_test::ProgramTestContext; use solana_sdk::{ diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 18728090f..7b6bb9385 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -1,6 +1,6 @@ use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::setup::{Solver, TestingContext, TransferDirection}; use crate::utils::auction::ActiveAuctionState; -use crate::utils::setup::TestingContext; use super::super::utils; use anchor_spl::token::spl_token; @@ -13,8 +13,7 @@ use solana_sdk::{ pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction, }; use std::rc::Rc; -use utils::setup::TransferDirection; -use utils::{constants::*, setup::Solver}; +use utils::constants::*; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; use wormhole_svm_definitions::{ solana::{ @@ -186,7 +185,7 @@ pub async fn execute_order_fallback( .get_new_latest_blockhash(test_context) .await .unwrap(); - utils::setup::fast_forward_slots(test_context, 3).await; + crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), @@ -232,7 +231,7 @@ pub async fn execute_order_fallback_test( active_auction_state, &testing_context.testing_actors.owner.pubkey(), &fixture_accounts, - testing_context.initial_testing_state.transfer_direction, + testing_context.transfer_direction, ); execute_order_fallback( testing_context, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 7148142ff..9eb82302b 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -2,8 +2,7 @@ use crate::testing_engine::config::ExpectedError; use crate::testing_engine::state::InitialOfferPlacedState; use super::super::utils; -use super::super::utils::setup::Solver; -use super::super::utils::setup::TestingContext; +use crate::testing_engine::setup::{Solver, TestingContext}; use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index d2a5bf990..cc872907f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -1,6 +1,6 @@ use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::testing_engine::state::TestingEngineState; -use crate::utils::setup::{TestingContext, TransferDirection}; use super::super::utils; use super::verify_shim::GuardianSignatureInfo; @@ -304,17 +304,16 @@ pub async fn prepare_order_response_test( .await .unwrap(); - let source_remote_token_messenger = - match testing_context.initial_testing_state.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; + let source_remote_token_messenger = match testing_context.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; let cctp_nonce = deposit.cctp_nonce; let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; @@ -367,7 +366,7 @@ pub async fn prepare_order_response_test( usdc_mint_address, &cctp_message_decoded, &guardian_signature_info, - &testing_context.initial_testing_state.transfer_direction, + &testing_context.transfer_direction, ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( testing_context, diff --git a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs index 1f2c7e7a2..8632feb71 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs @@ -1,5 +1,6 @@ +use crate::testing_engine::setup::TestingContext; +use crate::utils; use crate::utils::constants::*; -use crate::utils::{self, setup::TestingContext}; use anchor_lang::prelude::*; use anyhow::Result as AnyhowResult; diff --git a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs index fe1be2965..191eb7d86 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs @@ -1,9 +1,9 @@ use std::rc::Rc; use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::utils::account_fixtures::FixtureAccounts; use crate::utils::auction::{AuctionAccounts, AuctionState}; -use crate::utils::setup::{TestingContext, TransferDirection}; use anchor_lang::prelude::*; use anchor_lang::{InstructionData, ToAccountMetas}; use common::wormhole_cctp_solana::cctp::{ @@ -106,7 +106,7 @@ pub fn create_execute_order_shimless_accounts( Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; let token_messenger = Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let remote_token_messenger = match testing_context.initial_testing_state.transfer_direction { + let remote_token_messenger = match testing_context.transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.arbitrum_remote_token_messenger } @@ -160,7 +160,7 @@ pub async fn execute_order_shimless_test( payer_signer: &Rc, expected_error: Option<&ExpectedError>, ) -> Option { - crate::utils::setup::fast_forward_slots(test_context, 3).await; + crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; let fixture_accounts = testing_context .get_fixture_accounts() .expect("Fixture accounts not found"); diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index c31dee110..111c4a736 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -12,7 +12,7 @@ use solana_program::{bpf_loader_upgradeable, system_program}; use crate::testing_engine::config::ExpectedError; -use super::super::TestingContext; +use crate::testing_engine::setup::TestingContext; use anchor_lang::{InstructionData, ToAccountMetas}; use matching_engine::{ accounts::Initialize, diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index 2c7cd1aab..3d1ac115e 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -6,6 +6,7 @@ use super::super::utils; use anchor_lang::prelude::*; use anchor_lang::InstructionData; +use crate::testing_engine::setup::{Solver, TestingContext}; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; use matching_engine::accounts::{ @@ -22,7 +23,6 @@ use solana_sdk::signature::Keypair; use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; use utils::auction::{ActiveAuctionState, AuctionAccounts, AuctionOffer, AuctionState}; -use utils::setup::{Solver, TestingContext}; use utils::vaa::TestVaa; pub async fn place_initial_offer_shimless( diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 9044bea76..e626c1385 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -1,9 +1,9 @@ +use crate::testing_engine::config::ExpectedError; use crate::testing_engine::config::ExpectedLog; +use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::testing_engine::state::TestingEngineState; use crate::utils; use crate::utils::cctp_message::UsedNonces; -use crate::utils::setup::TestingContext; -use crate::{testing_engine::config::ExpectedError, utils::setup::TransferDirection}; use anchor_lang::InstructionData; use anchor_lang::{prelude::*, system_program}; use anchor_spl::token::spl_token; @@ -50,17 +50,16 @@ pub async fn prepare_order_response( .clone() .expect("Fixture accounts not found"); - let source_remote_token_messenger = - match testing_context.initial_testing_state.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; + let source_remote_token_messenger = match testing_context.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + } + _ => panic!("Unsupported transfer direction"), + }; let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; let custodian_address = testing_engine_state @@ -145,8 +144,7 @@ pub async fn prepare_order_response( }; let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let cctp_remote_token_messenger = match testing_context.initial_testing_state.transfer_direction - { + let cctp_remote_token_messenger = match testing_context.transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.ethereum_remote_token_messenger } diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index a356a9ab2..c04bc10c6 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -1,6 +1,6 @@ use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::setup::TestingContext; use crate::utils::auction::AuctionState; -use crate::utils::setup::TestingContext; use anchor_lang::prelude::*; use anchor_lang::InstructionData; diff --git a/solana/modules/matching-engine-testing/tests/integration_tests.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs similarity index 98% rename from solana/modules/matching-engine-testing/tests/integration_tests.rs rename to solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs index f3bc423c9..b5ed758a7 100644 --- a/solana/modules/matching-engine-testing/tests/integration_tests.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs @@ -1,27 +1,26 @@ #![allow(clippy::expect_used)] #![allow(clippy::panic)] -use anchor_lang::AccountDeserialize; -use anchor_spl::token::TokenAccount; -use matching_engine::error::MatchingEngineError; -use matching_engine::ID as PROGRAM_ID; -use solana_program_test::tokio; -use solana_sdk::pubkey::Pubkey; -use testing_engine::config::*; -mod shimful; -mod shimless; -mod testing_engine; -mod utils; +use crate::shimful; +use crate::shimless; +use crate::testing_engine; use crate::testing_engine::config::{ ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; -use crate::testing_engine::engine::{InstructionTrigger, TestingEngine}; +use crate::utils; +use anchor_lang::AccountDeserialize; +use anchor_spl::token::TokenAccount; +use matching_engine::ID as PROGRAM_ID; use shimful::post_message::set_up_post_message_transaction_test; use shimless::initialize::{initialize_program, AuctionParametersConfig}; +use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; use solana_sdk::transaction::TransactionError; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; use utils::router::add_local_router_endpoint_ix; -use utils::setup::{setup_environment, ShimMode, TestingContext, TransferDirection}; use utils::vaa::VaaArgs; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; @@ -443,6 +442,8 @@ pub async fn test_execute_order_shimless() { .execute(&mut test_context, instruction_triggers) .await; } + +#[tokio::test] pub async fn test_execute_order_fallback_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = VaaArgs { @@ -469,8 +470,8 @@ pub async fn test_execute_order_fallback_blocks_shimless() { InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { expected_error: Some(ExpectedError { instruction_index: 0, - error_code: MatchingEngineError::AccountAlreadyInitialized.into(), - error_string: MatchingEngineError::AccountAlreadyInitialized.to_string(), + error_code: 3012, + error_string: "AccountNotInitialized".to_string(), }), ..ExecuteOrderInstructionConfig::default() }), diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs new file mode 100644 index 000000000..b725a1f6d --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs @@ -0,0 +1 @@ +pub mod happy_path; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index c2b4975bb..a2d232270 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -4,6 +4,7 @@ use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; use anchor_lang::prelude::*; use solana_sdk::signature::Keypair; +/// An instruction config contains the configuration arguments for an instruction as well as the expected error pub trait InstructionConfig: Default { fn expected_error(&self) -> Option<&ExpectedError>; } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 0259c8d91..8a04eb5f9 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -1,6 +1,9 @@ use matching_engine::state::FastMarketOrder; use solana_program_test::ProgramTestContext; +use solana_sdk::signer::Signer; +use solana_sdk::transaction::Transaction; +use super::setup::TestingContext; use super::{config::*, state::*}; use crate::shimful; use crate::shimful::fast_market_order_shim::{ @@ -16,7 +19,6 @@ use crate::utils::{ // AuctionState }, router::create_all_router_endpoints_test, - setup::TestingContext, }; use anchor_lang::prelude::*; @@ -187,11 +189,8 @@ impl TestingEngine { .fixture_accounts .clone() .expect("Failed to get fixture accounts"); - let vaas: TestVaaPairs = self.testing_context.initial_testing_state.vaas.clone(); - let transfer_direction = self - .testing_context - .initial_testing_state - .transfer_direction; + let vaas: TestVaaPairs = self.testing_context.vaa_pairs.clone(); + let transfer_direction = self.testing_context.transfer_direction; TestingEngineState::Uninitialized(BaseState { fixture_accounts, vaas, @@ -848,3 +847,41 @@ impl TestingEngine { } } } + +/// Fast forwards the slot in the test context +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `num_slots` - The number of slots to fast forward +pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots: u64) { + // Get the current slot + let mut current_slot = test_context.banks_client.get_root_slot().await.unwrap(); + + let target_slot = current_slot.saturating_add(num_slots); + while current_slot < target_slot { + // Warp to the next slot - note we need to borrow_mut() here + test_context + .warp_to_slot(current_slot.saturating_add(1)) + .expect("Failed to warp to slot"); + current_slot = current_slot.saturating_add(1); + } + + // Optionally, process a transaction to ensure the new slot is recognized + let recent_blockhash = test_context.last_blockhash; + let payer = test_context.payer.pubkey(); + let tx = Transaction::new_signed_with_payer( + &[], + Some(&payer), + &[&test_context.payer], + recent_blockhash, + ); + + test_context + .banks_client + .process_transaction(tx) + .await + .expect("Failed to process transaction after warping"); + + println!("Fast forwarded {} slots", num_slots); +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/mod.rs b/solana/modules/matching-engine-testing/tests/testing_engine/mod.rs index 09052261d..ccf788b02 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/mod.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/mod.rs @@ -2,4 +2,5 @@ pub mod config; pub mod engine; +pub mod setup; pub mod state; diff --git a/solana/modules/matching-engine-testing/tests/utils/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs similarity index 89% rename from solana/modules/matching-engine-testing/tests/utils/setup.rs rename to solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 2da1c10c3..d591385b9 100644 --- a/solana/modules/matching-engine-testing/tests/utils/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -1,21 +1,20 @@ -use super::account_fixtures::FixtureAccounts; -use super::airdrop::airdrop; -use super::auction::AuctionState; -use super::mint::MintFixture; -use super::program_fixtures::{ +use crate::testing_engine::config::{ExpectedError, ExpectedLog}; +use crate::utils::account_fixtures::FixtureAccounts; +use crate::utils::airdrop::airdrop; +use crate::utils::mint::MintFixture; +use crate::utils::program_fixtures::{ initialise_cctp_message_transmitter, initialise_cctp_token_messenger_minter, initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, initialise_verify_shims, initialise_wormhole_core_bridge, }; -use super::vaa::{ +use crate::utils::vaa::{ create_vaas_test_with_chain_and_address, ChainAndAddress, TestVaaPair, TestVaaPairs, VaaArgs, }; -use super::{ +use crate::utils::{ airdrop::airdrop_usdc, token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, }; -use super::{Chain, REGISTERED_TOKEN_ROUTERS}; -use crate::testing_engine::config::{ExpectedError, ExpectedLog}; +use crate::utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use anchor_lang::AccountDeserialize; use anchor_spl::token::{ spl_token::{self, instruction::approve}, @@ -138,12 +137,13 @@ impl PreTestingContext { /// * `program_data_account` - The pubkey of the program data account created by the Upgrade Manager /// * `testing_actors` - The testing actors, including solvers and the owner /// * `fixture_accounts` - The accounts that are loaded from files under the `tests/fixtures` directory -/// * `testing_state` - The testing state, including the auction state and the Vaas +/// * `vaa_pairs` - The Vaas that were created in the pre-testing context setup stage pub struct TestingContext { pub program_data_account: Pubkey, pub testing_actors: TestingActors, pub fixture_accounts: Option, - pub initial_testing_state: TestingState, + pub vaa_pairs: TestVaaPairs, + pub transfer_direction: TransferDirection, } impl TestingContext { @@ -179,23 +179,17 @@ impl TestingContext { .testing_actors .create_atas(&mut test_context, USDC_MINT_ADDRESS) .await; - let initial_testing_state = match vaas_test { - Some(vaas_test) => TestingState { - vaas: vaas_test, - transfer_direction, - ..TestingState::default() - }, - None => TestingState { - transfer_direction, - ..TestingState::default() - }, + let vaa_pairs = match vaas_test { + Some(vaas_test) => vaas_test, + None => TestVaaPairs::new(), }; ( TestingContext { program_data_account: pre_testing_context.program_data_pubkey, testing_actors: pre_testing_context.testing_actors, fixture_accounts: Some(pre_testing_context.account_fixtures), - initial_testing_state, + vaa_pairs, + transfer_direction, }, test_context, ) @@ -207,10 +201,7 @@ impl TestingContext { /// /// * `test_context` - The test context pub async fn verify_vaas(&self, test_context: &mut ProgramTestContext) { - self.initial_testing_state - .vaas - .verify_posted_vaas(test_context) - .await; + self.vaa_pairs.verify_posted_vaas(test_context).await; } /// Gets the VAA pair at the given index @@ -219,8 +210,8 @@ impl TestingContext { /// /// * `index` - The index of the VAA pair pub fn get_vaa_pair(&self, index: usize) -> Option { - if index < self.initial_testing_state.vaas.len() { - Some(self.initial_testing_state.vaas[index].clone()) + if index < self.vaa_pairs.len() { + Some(self.vaa_pairs[index].clone()) } else { None } @@ -617,66 +608,6 @@ impl TestingActors { } } -/// Fast forwards the slot in the test context -/// -/// # Arguments -/// -/// * `test_context` - The test context -/// * `num_slots` - The number of slots to fast forward -pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots: u64) { - // Get the current slot - let mut current_slot = test_context.banks_client.get_root_slot().await.unwrap(); - - let target_slot = current_slot.saturating_add(num_slots); - while current_slot < target_slot { - // Warp to the next slot - note we need to borrow_mut() here - test_context - .warp_to_slot(current_slot.saturating_add(1)) - .expect("Failed to warp to slot"); - current_slot = current_slot.saturating_add(1); - } - - // Optionally, process a transaction to ensure the new slot is recognized - let recent_blockhash = test_context.last_blockhash; - let payer = test_context.payer.pubkey(); - let tx = Transaction::new_signed_with_payer( - &[], - Some(&payer), - &[&test_context.payer], - recent_blockhash, - ); - - test_context - .banks_client - .process_transaction(tx) - .await - .expect("Failed to process transaction after warping"); - - println!("Fast forwarded {} slots", num_slots); -} - -/// A struct representing the testing state -/// -/// # Fields -/// -/// * `auction_state` - The auction state -/// * `vaas` - The VAAs -pub struct TestingState { - pub auction_state: AuctionState, - pub vaas: TestVaaPairs, - pub transfer_direction: TransferDirection, -} - -impl Default for TestingState { - fn default() -> Self { - Self { - auction_state: AuctionState::Inactive, - vaas: TestVaaPairs::new(), - transfer_direction: TransferDirection::FromEthereumToArbitrum, - } - } -} - /// The mode of the shim /// /// # Enums diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index bb530ffb0..45dcf0061 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -1,8 +1,8 @@ +use super::setup::TransferDirection; use crate::utils::{ account_fixtures::FixtureAccounts, auction::{AuctionAccounts, AuctionState}, router::TestRouterEndpoints, - setup::TransferDirection, vaa::{TestVaaPair, TestVaaPairs}, }; use anchor_lang::prelude::*; diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 87294bbc7..ec8b6fc29 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -2,8 +2,8 @@ use anchor_lang::prelude::*; use solana_program_test::ProgramTestContext; use super::router::TestRouterEndpoints; -use super::setup::{Solver, TestingContext, TransferDirection}; use super::Chain; +use crate::testing_engine::setup::{Solver, TestingContext, TransferDirection}; use anyhow::{anyhow, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; #[derive(Clone)] @@ -111,7 +111,7 @@ impl ActiveAuctionState { custody_token_bump: 254, // TODO: Figure this out vaa_sequence: 0, // No need to cehck against this source_chain: { - match testing_context.initial_testing_state.transfer_direction { + match testing_context.transfer_direction { TransferDirection::FromEthereumToArbitrum => 3, TransferDirection::FromArbitrumToEthereum => 23, TransferDirection::Other => { diff --git a/solana/modules/matching-engine-testing/tests/utils/mod.rs b/solana/modules/matching-engine-testing/tests/utils/mod.rs index a3d6a3cd7..1603c1b75 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mod.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mod.rs @@ -8,8 +8,8 @@ pub mod cctp_message; pub mod constants; pub mod mint; pub mod program_fixtures; +pub mod public_keys; pub mod router; -pub mod setup; pub mod token_account; pub mod vaa; pub use constants::*; diff --git a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs new file mode 100644 index 000000000..6decae147 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs @@ -0,0 +1,142 @@ +use solana_sdk::{keccak, pubkey::Pubkey}; + +use super::Chain; + +pub trait ToBytes { + fn to_bytes(&self) -> [u8; 32]; +} + +/// A struct representing a test public key +/// +/// # Enums +/// +/// * `solana` - A Solana public key +/// * `evm` - An EVM public key +/// * `bytes` - A bytes representation of the public key +/// +/// # Methods +/// +/// * `to_bytes` - Converts the public key to a bytes array +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub enum TestPubkey { + Solana(Pubkey), + Evm(EvmAddress), + Bytes([u8; 32]), +} + +impl ToBytes for TestPubkey { + fn to_bytes(&self) -> [u8; 32] { + match self { + TestPubkey::Solana(pubkey) => pubkey.to_bytes(), + TestPubkey::Evm(evm_address) => evm_address.to_bytes(), + TestPubkey::Bytes(bytes) => *bytes, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvmAddress([u8; 20]); + +#[allow(dead_code)] +impl EvmAddress { + pub fn new(bytes: [u8; 20]) -> Self { + Self(bytes) + } + + pub fn from_hex(hex: &str) -> Option { + let hex = hex.strip_prefix("0x").unwrap_or_else(|| hex); + let bytes = hex::decode(hex).ok()?; + if bytes.len() != 20 { + return None; + } + let mut array = [0u8; 20]; + array.copy_from_slice(&bytes); + Some(Self(array)) + } + + pub fn as_bytes(&self) -> &[u8; 20] { + &self.0 + } + + pub fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self.0)) + } + + pub fn new_unique() -> Self { + let (_secp_secret_key, secp_pubkey) = + secp256k1::generate_keypair(&mut secp256k1::rand::rngs::OsRng); + // Get uncompressed public key bytes (65 bytes: prefix + x + y) + let uncompressed = secp_pubkey.serialize_uncompressed(); + // Hash with Keccak-256 removing the prefix + let hash = keccak::hashv(&[&uncompressed[1..]]); + // Address is the last 20 bytes of the hash + let address: [u8; 20] = hash.as_ref()[12..].try_into().unwrap(); + Self(address) + } +} + +impl ToBytes for EvmAddress { + fn to_bytes(&self) -> [u8; 32] { + // Pad the evm address with 12 zero bytes + let mut bytes = vec![0u8; 12]; + bytes.extend_from_slice(&self.0); + bytes.try_into().unwrap() + } +} + +/// A struct representing a chain and address +/// +/// # Fields +/// +/// * `chain` - The chain +/// * `address` - The address +#[derive(Clone)] +pub struct ChainAddress { + pub chain: Chain, + pub address: TestPubkey, +} + +impl ChainAddress { + #[allow(dead_code)] + pub fn new_unique(chain: Chain) -> Self { + match chain { + Chain::Solana => Self { + chain, + address: TestPubkey::Solana(Pubkey::new_unique()), + }, + Chain::Ethereum => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Arbitrum => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Avalanche => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Optimism => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Polygon => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + Chain::Base => Self { + chain, + address: TestPubkey::Evm(EvmAddress::new_unique()), + }, + } + } + + #[allow(dead_code)] + pub fn new_with_address(chain: Chain, address: [u8; 32]) -> Self { + Self { + chain, + address: TestPubkey::Bytes(address), + } + } +} diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index 9bd796f42..db10beebf 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -1,8 +1,8 @@ // Add methods for adding endpoints to the program test use super::constants::*; -use super::setup::TestingContext; use super::token_account::create_token_account_for_pda; +use crate::testing_engine::setup::TestingContext; use anchor_lang::prelude::*; use anchor_lang::{InstructionData, ToAccountMetas}; diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index 3ecd56265..b9a37d751 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -8,6 +8,7 @@ use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use super::constants::Chain; use super::constants::CORE_BRIDGE_PID; +use super::public_keys::{ChainAddress, ToBytes}; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use solana_program::keccak; @@ -805,141 +806,3 @@ pub fn create_vaas_test_with_chain_and_address( ); test_fast_transfers } -pub trait ToBytes { - fn to_bytes(&self) -> [u8; 32]; -} - -/// A struct representing a test public key -/// -/// # Enums -/// -/// * `solana` - A Solana public key -/// * `evm` - An EVM public key -/// * `bytes` - A bytes representation of the public key -/// -/// # Methods -/// -/// * `to_bytes` - Converts the public key to a bytes array -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub enum TestPubkey { - Solana(Pubkey), - Evm(EvmAddress), - Bytes([u8; 32]), -} - -impl ToBytes for TestPubkey { - fn to_bytes(&self) -> [u8; 32] { - match self { - TestPubkey::Solana(pubkey) => pubkey.to_bytes(), - TestPubkey::Evm(evm_address) => evm_address.to_bytes(), - TestPubkey::Bytes(bytes) => *bytes, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EvmAddress([u8; 20]); - -#[allow(dead_code)] -impl EvmAddress { - pub fn new(bytes: [u8; 20]) -> Self { - Self(bytes) - } - - pub fn from_hex(hex: &str) -> Option { - let hex = hex.strip_prefix("0x").unwrap_or_else(|| hex); - let bytes = hex::decode(hex).ok()?; - if bytes.len() != 20 { - return None; - } - let mut array = [0u8; 20]; - array.copy_from_slice(&bytes); - Some(Self(array)) - } - - pub fn as_bytes(&self) -> &[u8; 20] { - &self.0 - } - - pub fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self.0)) - } - - pub fn new_unique() -> Self { - let (_secp_secret_key, secp_pubkey) = - secp256k1::generate_keypair(&mut secp256k1::rand::rngs::OsRng); - // Get uncompressed public key bytes (65 bytes: prefix + x + y) - let uncompressed = secp_pubkey.serialize_uncompressed(); - // Hash with Keccak-256 removing the prefix - let hash = keccak::hashv(&[&uncompressed[1..]]); - // Address is the last 20 bytes of the hash - let address: [u8; 20] = hash.as_ref()[12..].try_into().unwrap(); - Self(address) - } -} - -impl ToBytes for EvmAddress { - fn to_bytes(&self) -> [u8; 32] { - // Pad the evm address with 12 zero bytes - let mut bytes = vec![0u8; 12]; - bytes.extend_from_slice(&self.0); - bytes.try_into().unwrap() - } -} - -/// A struct representing a chain and address -/// -/// # Fields -/// -/// * `chain` - The chain -/// * `address` - The address -#[derive(Clone)] -pub struct ChainAddress { - pub chain: Chain, - pub address: TestPubkey, -} - -impl ChainAddress { - #[allow(dead_code)] - pub fn new_unique(chain: Chain) -> Self { - match chain { - Chain::Solana => Self { - chain, - address: TestPubkey::Solana(Pubkey::new_unique()), - }, - Chain::Ethereum => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - Chain::Arbitrum => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - Chain::Avalanche => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - Chain::Optimism => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - Chain::Polygon => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - Chain::Base => Self { - chain, - address: TestPubkey::Evm(EvmAddress::new_unique()), - }, - } - } - - #[allow(dead_code)] - pub fn new_with_address(chain: Chain, address: [u8; 32]) -> Self { - Self { - chain, - address: TestPubkey::Bytes(address), - } - } -} From 5828ea120cbfe7ef116b59cbeeabe5b5294e02ff Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 2 Apr 2025 02:22:40 +0100 Subject: [PATCH 051/112] added some negative tests, some more refactoring --- .../matching-engine-testing/tests/README.md | 22 +- .../matching-engine-testing/tests/mod.rs | 1 - .../tests/shimful/README.md | 7 + .../tests/shimful/shims_make_offer.rs | 14 +- .../tests/shimless/README.md | 8 + .../tests/shimless/initialize.rs | 33 +- .../tests/shimless/make_offer.rs | 48 +- .../tests/shimless/prepare_order_response.rs | 29 +- .../tests/shimless/settle_auction.rs | 17 + .../create_and_close_fast_market_order.rs | 234 +++++++ .../tests/test_scenarios/execute_order.rs | 125 ++++ .../tests/test_scenarios/happy_path.rs | 655 ------------------ .../test_scenarios/initialise_and_misc.rs | 251 +++++++ .../tests/test_scenarios/make_offer.rs | 312 +++++++++ .../tests/test_scenarios/mod.rs | 7 +- .../tests/test_scenarios/prepare_order.rs | 175 +++++ .../tests/test_scenarios/settle_auction.rs | 55 ++ .../tests/testing_engine/config.rs | 87 ++- .../tests/testing_engine/engine.rs | 111 ++- .../tests/testing_engine/setup.rs | 125 +++- .../tests/testing_engine/state.rs | 14 + .../tests/utils/auction.rs | 93 ++- 22 files changed, 1673 insertions(+), 750 deletions(-) create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs delete mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs create mode 100644 solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs diff --git a/solana/modules/matching-engine-testing/tests/README.md b/solana/modules/matching-engine-testing/tests/README.md index 4b9378673..6d750d772 100644 --- a/solana/modules/matching-engine-testing/tests/README.md +++ b/solana/modules/matching-engine-testing/tests/README.md @@ -2,7 +2,9 @@ ## How to read the tests -Each test is found in the `integration_tests.rs` file. +Each test is found in the `test_scenarios` directory. + +Each file in this directory is a module that contains tests for a specific subset of scenarios (instructions). Each test is a function that is annotated with `#[tokio::test]`. @@ -12,7 +14,7 @@ The `TestingEngine` is initialised with a `TestingContext`. The `TestingContext` The `TestingEngine` is used to execute the instruction triggers in the order they are provided. See the `testing_engine/engine.rs` file for more details. -## Integration Tests +## Happy path integration tests ### Initialize program @@ -40,4 +42,20 @@ What is expected: - Guardian set is closed - Close account refund recipient is sent usdc +### Place initial offer (shimless) + +What is expected: +- Fast market order is initialised +- Initial offer is placed +- Auction account is created and corresponds to a vaa and the initial offer + +### Place initial offer (shim) + +What is expected: +- Fast market order is posted as a vaa +- Initial offer is placed +- Auction account is created and corresponds to a vaa and the initial offer + + + diff --git a/solana/modules/matching-engine-testing/tests/mod.rs b/solana/modules/matching-engine-testing/tests/mod.rs index 301013a66..021ffd057 100644 --- a/solana/modules/matching-engine-testing/tests/mod.rs +++ b/solana/modules/matching-engine-testing/tests/mod.rs @@ -3,7 +3,6 @@ mod shimful; mod shimless; -// Where tests are located mod test_scenarios; mod testing_engine; mod utils; diff --git a/solana/modules/matching-engine-testing/tests/shimful/README.md b/solana/modules/matching-engine-testing/tests/shimful/README.md index bf885461c..f0ed5f00b 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/README.md +++ b/solana/modules/matching-engine-testing/tests/shimful/README.md @@ -2,3 +2,10 @@ This directory contains tests that use the fallback program. +## Files + +- `fast_market_order_shim.rs` - A function that creates a fast market order account and one that closes it +- `shims_make_offer.rs` - A function that places an initial offer and one that improves an offer +- `shims_execute_order.rs` - A function that executes an order +- `shims_prepare_order_response.rs` - A function that prepares an order response +- `shims_settle_auction.rs` - A function that settles an auction \ No newline at end of file diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 9eb82302b..5d3256386 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -2,7 +2,7 @@ use crate::testing_engine::config::ExpectedError; use crate::testing_engine::state::InitialOfferPlacedState; use super::super::utils; -use crate::testing_engine::setup::{Solver, TestingContext}; +use crate::testing_engine::setup::{TestingActor, TestingContext}; use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, @@ -42,14 +42,14 @@ pub async fn place_initial_offer_fallback( test_context: &mut ProgramTestContext, payer_signer: &Rc, vaa_data: &utils::vaa::PostedVaaData, - solver: Solver, + actor: TestingActor, fast_market_order_account: &Pubkey, auction_accounts: &utils::auction::AuctionAccounts, offer_price: u64, expected_error: Option<&ExpectedError>, ) -> Option { let program_id = testing_context.get_matching_engine_program_id(); - let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, solver.pubkey()); + let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, actor.pubkey()); let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.digest()], @@ -76,11 +76,11 @@ pub async fn place_initial_offer_fallback( ) .0; - solver + actor .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; - let solver_usdc_balance_before = solver.get_balance(test_context).await; + let actor_usdc_balance_before = actor.get_token_account_balance(test_context).await; let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price); @@ -122,9 +122,9 @@ pub async fn place_initial_offer_fallback( .execute_and_verify_transaction(test_context, transaction, expected_error) .await; if expected_error.is_none() { - let solver_usdc_balance_after = solver.get_balance(test_context).await; + let actor_usdc_balance_after = actor.get_token_account_balance(test_context).await; assert!( - solver_usdc_balance_after < solver_usdc_balance_before, + actor_usdc_balance_after < actor_usdc_balance_before, "Solver USDC balance should have decreased" ); let new_active_auction_state = utils::auction::ActiveAuctionState { diff --git a/solana/modules/matching-engine-testing/tests/shimless/README.md b/solana/modules/matching-engine-testing/tests/shimless/README.md index 2e8466673..c636365d9 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/README.md +++ b/solana/modules/matching-engine-testing/tests/shimless/README.md @@ -2,3 +2,11 @@ This directory contains tests that do not use the fallback program. +## Files + +- `place_initial_offer.rs` - A function that places an initial offer +- `make_offer.rs` - A function that places an initial offer and one that improves an offer +- `execute_order.rs` - A function that executes an order +- `prepare_order_response.rs` - A function that prepares an order response +- `settle_auction.rs` - A function that settles an auction + diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 111c4a736..97eb98add 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -10,7 +10,7 @@ use anchor_lang::AccountDeserialize; use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; use solana_program::{bpf_loader_upgradeable, system_program}; -use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::{ExpectedError, ExpectedLog}; use crate::testing_engine::setup::TestingContext; use anchor_lang::{InstructionData, ToAccountMetas}; @@ -142,6 +142,7 @@ pub async fn initialize_program( test_context: &mut ProgramTestContext, auction_parameters_config: AuctionParametersConfig, expected_error: Option<&ExpectedError>, + expected_log_messages: Option<&Vec>, ) -> Option { let program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = testing_context.get_usdc_mint_address(); @@ -149,7 +150,6 @@ pub async fn initialize_program( let (custodian, _custodian_bump) = Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); - // TODO: Figure out this seed? Where does the 0u32 come from? let (auction_config, _auction_config_bump) = Pubkey::find_program_address( &[ AuctionConfig::SEED_PREFIX, @@ -218,6 +218,35 @@ pub async fn initialize_program( .execute_and_verify_transaction(test_context, versioned_transaction, expected_error) .await; + if let Some(expected_log_messages) = expected_log_messages { + // Recreate the instruction + let instruction = Instruction { + program_id, + accounts: accounts.to_account_metas(None), + data: ix_data.data(), + }; + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&test_context.payer.pubkey())); + let new_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .expect("Could not get new blockhash"); + transaction.sign( + &[ + &test_context.payer, + &testing_context.testing_actors.owner.keypair(), + ], + new_blockhash, + ); + let versioned_transaction = VersionedTransaction::from(transaction); + + // Simulate and verify logs + testing_context + .simulate_and_verify_logs(test_context, versioned_transaction, expected_log_messages) + .await + .expect("Failed to verify logs"); + } + if expected_error.is_none() { // Verify the results let custodian_account = test_context diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index 3d1ac115e..c42faaa93 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -25,6 +25,23 @@ use solana_sdk::transaction::Transaction; use utils::auction::{ActiveAuctionState, AuctionAccounts, AuctionOffer, AuctionState}; use utils::vaa::TestVaa; +/// Place an initial offer (shimless) +/// +/// Place an initial offer by providing a price. +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `accounts` - The auction accounts +/// * `fast_market_order` - The fast market order +/// * `offer_price` - The price of the offer +/// * `payer_signer` - The payer signer +/// * `expected_error` - The expected error +/// +/// # Returns +/// +/// The new auction state if successful, otherwise the old auction state pub async fn place_initial_offer_shimless( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -76,7 +93,7 @@ pub async fn place_initial_offer_shimless( .0; { // Check if solver has already approved usdc - let usdc_account = accounts.solver.token_account_address().unwrap(); + let usdc_account = accounts.actor.token_account_address().unwrap(); let usdc_account_info = test_context .banks_client .get_account(usdc_account) @@ -89,14 +106,14 @@ pub async fn place_initial_offer_shimless( .expect("Failed to deserialize usdc account"); if token_account_info.delegate.is_none() { accounts - .solver + .actor .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; } else { let delegate = token_account_info.delegate.unwrap(); if delegate != transfer_authority { accounts - .solver + .actor .approve_usdc(test_context, &transfer_authority, 420_000__000_000) .await; } @@ -173,6 +190,23 @@ pub async fn place_initial_offer_shimless( } } +/// Improve an offer (shimless) +/// +/// Improve an offer by providing a new price. +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `solver` - The solver +/// * `offer_price` - The new price +/// * `payer_signer` - The payer signer +/// * `initial_auction_state` - The initial auction state +/// * `expected_error` - The expected error +/// +/// # Returns +/// +/// The new auction state if successful, otherwise the old auction state pub async fn improve_offer( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -181,7 +215,7 @@ pub async fn improve_offer( payer_signer: &Rc, initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, -) -> Option { +) -> AuctionState { let program_id = testing_context.get_matching_engine_program_id(); let active_auction_state = initial_auction_state.get_active_auction().unwrap(); let auction_config = active_auction_state.auction_config_address; @@ -254,7 +288,7 @@ pub async fn improve_offer( .get_active_auction() .unwrap() .initial_offer; - Some(AuctionState::Active(Box::new(ActiveAuctionState { + AuctionState::Active(Box::new(ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: auction_config, @@ -264,8 +298,8 @@ pub async fn improve_offer( offer_token, offer_price, }, - }))) + })) } else { - None + initial_auction_state.clone() } } diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index e626c1385..6ffeddbd1 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -30,6 +30,25 @@ pub struct PrepareOrderResponseFixture { pub prepared_custody_token: Pubkey, } +/// Prepare an order response (shimless) +/// +/// Prepare an order response by providing a fast market order. +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `payer_signer` - The payer signer +/// * `testing_engine_state` - The testing engine state +/// * `to_endpoint_address` - The to endpoint address +/// * `from_endpoint_address` - The from endpoint address +/// * `base_fee_token_address` - The base fee token address +/// * `expected_error` - The expected error +/// * `expected_log_messages` - The expected log messages +/// +/// # Returns +/// +/// The prepared order response fixture if successful, otherwise None #[allow(clippy::too_many_arguments)] pub async fn prepare_order_response( testing_context: &TestingContext, @@ -40,7 +59,7 @@ pub async fn prepare_order_response( from_endpoint_address: &Pubkey, base_fee_token_address: &Pubkey, expected_error: Option<&ExpectedError>, - expected_log_message: Option<&Vec>, + expected_log_messages: Option<&Vec>, ) -> Option { let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); @@ -206,13 +225,9 @@ pub async fn prepare_order_response( .await .expect("Failed to get new blockhash"), ); - if let Some(expected_log_message) = expected_log_message { - assert!( - expected_error.is_none(), - "Expected error is not allowed when expected log message is provided" - ); + if let Some(expected_log_messages) = expected_log_messages { testing_context - .simulate_and_verify_logs(test_context, transaction, expected_log_message) + .simulate_and_verify_logs(test_context, transaction, expected_log_messages) .await .unwrap(); } else { diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index c04bc10c6..3b819058f 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -14,6 +14,23 @@ use solana_sdk::transaction::Transaction; use std::rc::Rc; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; +/// Settle an auction (shimless) +/// +/// Settle an auction by providing a prepare order response address, prepared custody token, and expected error. +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `payer_signer` - The payer signer +/// * `auction_state` - The auction state +/// * `prepare_order_response_address` - The prepare order response address +/// * `prepared_custody_token` - The prepared custody token +/// * `expected_error` - The expected error +/// +/// # Returns +/// +/// The new auction state if successful, otherwise the old auction state pub async fn settle_auction_complete( testing_context: &TestingContext, test_context: &mut ProgramTestContext, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs new file mode 100644 index 000000000..8e9d44590 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs @@ -0,0 +1,234 @@ +//! # Happy path instruction testing +//! +//! This module contains tests for the create and close fast market order instructions. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! +//! - `test_initialise_fast_market_order_fallback` - Test that the fast market order is initialised correctly +//! - `test_close_fast_market_order_fallback` - Test that the fast market order is closed correctly +//! - `test_close_fast_market_order_fallback_with_custom_refund_recipient` - Test that the fast market order is closed correctly with a custom refund recipient +//! +//! ### Sad path tests +//! +//! - `test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialise_it` - Test that the fast market order cannot be refunded by someone who did not initialise it +//! +//! ### Edge case tests +//! +use crate::testing_engine; +use crate::utils; +use matching_engine::error::MatchingEngineError; +use solana_program_test::tokio; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::vaa::VaaArgs; + +/* + Happy path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** * * ** +** ** ** ** + ** **** **** ** + ** ** ** ** + ** *** *** ** + *** **** **** *** + ** ****** ****** ** + *** *************** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + +/// Test that the create fast market order account works correctly for the fallback instruction +#[tokio::test] +pub async fn test_initialise_fast_market_order_fallback() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the close fast market order account works correctly for the fallback instruction +#[tokio::test] +pub async fn test_close_fast_market_order_fallback() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the close fast market order account works correctly for the fallback instruction +#[tokio::test] +pub async fn test_close_fast_market_order_fallback_with_custom_refund_recipient() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let solver_1 = &testing_context.testing_actors.solvers[1].clone(); + let solver_1_balance_before = solver_1.get_lamport_balance(&mut test_context).await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + close_account_refund_recipient: Some(solver_1.pubkey()), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some(solver_1.keypair()), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; + let solver_1_balance_after = solver_1.get_lamport_balance(&mut test_context).await; + assert!( + solver_1_balance_after > solver_1_balance_before, + "Solver 1 balance after is not greater than balance before" + ); +} + +/* + Sad path tests Section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** ** +** ** +** ** + ** ************ ** + ** ****** ****** ** + *** ***** ***** *** + ** *** *** ** + *** ** ** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + +/// Test that the fast market order cannot be refunded by someone who did not initialise it +#[tokio::test] +pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialise_it() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let solver_0 = testing_context.testing_actors.solvers.first().unwrap(); + let solver_1 = testing_context.testing_actors.solvers.last().unwrap(); + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + close_account_refund_recipient: Some(solver_0.pubkey()), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some(solver_1.keypair()), + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::MismatchingCloseAccountRefundRecipient), + error_string: "Fast market order account owner is invalid".to_string(), + }), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + ]; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/* + Edge case tests section + 88 + 88 + 88 + ,adPPYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYb,88 +a8" "" a8P_____88 88P' `"8a I8[ "" a8" "8a 88P' "Y8 a8P_____88 a8" `Y88 +8b 8PP""""""" 88 88 `"Y8ba, 8b d8 88 8PP""""""" 8b 88 +"8a, ,aa "8b, ,aa 88 88 aa ]8I "8a, ,a8" 88 "8b, ,aa "8a, ,d88 + `"Ybbd8"' `"Ybbd8"' 88 88 `"YbbdP"' `"YbbdP"' 88 `"Ybbd8"' `"8bbdP"Y8 +*/ diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs new file mode 100644 index 000000000..8d61285f9 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -0,0 +1,125 @@ +//! # Execute order instruction testing +//! +//! This module contains tests for the execute order instruction. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! - `test_execute_order_fallback` - Test that the execute order fallback instruction works correctly +//! - `test_execute_order_shimless` - Test that the execute order shimless instruction works correctly +//! + +use crate::testing_engine; +use crate::testing_engine::config::{ + InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, +}; +use crate::utils; + +use solana_program_test::tokio; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::vaa::VaaArgs; + +/// Test that the execute order fallback instruction works correctly +#[tokio::test] +// TODO: Flesh out this test to see if the message was posted correctly +pub async fn test_execute_order_fallback() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the execute order shimless instruction works correctly +#[tokio::test] +pub async fn test_execute_order_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/* + Sad path tests +*/ + +/// Test that the execute order fallback instruction blocks the shimless instruction +#[tokio::test] +pub async fn test_execute_order_fallback_blocks_shimless() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 3012, + error_string: "AccountNotInitialized".to_string(), + }), + ..ExecuteOrderInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs deleted file mode 100644 index b5ed758a7..000000000 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/happy_path.rs +++ /dev/null @@ -1,655 +0,0 @@ -#![allow(clippy::expect_used)] -#![allow(clippy::panic)] - -use crate::shimful; -use crate::shimless; -use crate::testing_engine; -use crate::testing_engine::config::{ - ExpectedError, ImproveOfferInstructionConfig, InitializeInstructionConfig, - PlaceInitialOfferInstructionConfig, -}; -use crate::utils; -use anchor_lang::AccountDeserialize; -use anchor_spl::token::TokenAccount; -use matching_engine::ID as PROGRAM_ID; -use shimful::post_message::set_up_post_message_transaction_test; -use shimless::initialize::{initialize_program, AuctionParametersConfig}; -use solana_program_test::tokio; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::transaction::TransactionError; -use testing_engine::config::*; -use testing_engine::engine::{InstructionTrigger, TestingEngine}; -use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; -use utils::router::add_local_router_endpoint_ix; -use utils::vaa::VaaArgs; -use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; - -/// Test that the program is initialised correctly -#[tokio::test] -pub async fn test_initialize_program() { - let (testing_context, mut test_context) = setup_environment( - ShimMode::None, - TransferDirection::FromArbitrumToEthereum, - None, // Vaa args for creating vaas - ) - .await; - - let initialize_config = InitializeInstructionConfig::default(); - - let testing_engine = TestingEngine::new(testing_context).await; - - testing_engine - .execute( - &mut test_context, - vec![InstructionTrigger::InitializeProgram(initialize_config)], - ) - .await; -} - -/// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains -#[tokio::test] -pub async fn test_cctp_token_router_endpoint_creation() { - let (testing_context, mut test_context) = setup_environment( - ShimMode::None, // Shim mode - TransferDirection::FromArbitrumToEthereum, // Transfer direction - None, // Vaa args - ) - .await; - - let initialize_config = InitializeInstructionConfig::default(); - - let testing_engine = TestingEngine::new(testing_context).await; - - testing_engine - .execute( - &mut test_context, - vec![InstructionTrigger::InitializeProgram(initialize_config)], - ) - .await; -} - -#[tokio::test] -pub async fn test_local_token_router_endpoint_creation() { - let (testing_context, mut test_context) = setup_environment( - ShimMode::None, - TransferDirection::FromArbitrumToEthereum, - None, - ) - .await; - - let initialize_fixture = initialize_program( - &testing_context, - &mut test_context, - AuctionParametersConfig::default(), - None, - ) - .await - .expect("Failed to initialize program"); - let payer_signer = testing_context.testing_actors.owner.keypair(); - let _local_token_router_endpoint = add_local_router_endpoint_ix( - &testing_context, - &mut test_context, - &payer_signer, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair().as_ref(), - ) - .await; -} - -/// Test setting up vaas -/// Vaa is from arbitrum to ethereum -#[tokio::test] -pub async fn test_setup_vaas() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifySignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - testing_context.verify_vaas(&mut test_context).await; - - let testing_engine = TestingEngine::new(testing_context).await; - testing_engine - .execute( - &mut test_context, - vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - ], - ) - .await; -} - -#[tokio::test] -pub async fn test_post_message_shims() { - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - None, - ) - .await; - let actors = &testing_context.testing_actors; - let emitter_signer = actors.owner.keypair(); - let payer_signer = actors.solvers[0].keypair(); - set_up_post_message_transaction_test( - &testing_context, - &mut test_context, - &payer_signer, - &emitter_signer, - ) - .await; -} - -#[tokio::test] -pub async fn test_initialise_fast_market_order_fallback() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - ]; - - let testing_engine = TestingEngine::new(testing_context).await; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_close_fast_market_order_fallback() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::CloseFastMarketOrderShim( - CloseFastMarketOrderShimInstructionConfig::default(), - ), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_approve_usdc() { - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - TransferDirection::FromArbitrumToEthereum, - Some(vaa_args), - ) - .await; - let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; - let vaa_data = first_test_ft.vaa_data; - - let actors = &testing_context.testing_actors; - let solver = actors.solvers[0].clone(); - let offer_price: u64 = 1__000_000; - let program_id = PROGRAM_ID; - let new_pubkey = Pubkey::new_unique(); - - let transfer_authority = Pubkey::find_program_address( - &[ - common::TRANSFER_AUTHORITY_SEED_PREFIX, - &new_pubkey.to_bytes(), - &offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; - solver - .approve_usdc(&mut test_context, &transfer_authority, offer_price) - .await; - - let usdc_balance = solver.get_balance(&mut test_context).await; - - // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. - let _guardian_signature_info = shimful::verify_shim::create_guardian_signatures( - &testing_context, - &mut test_context, - &actors.owner.keypair(), - &vaa_data, - &CORE_BRIDGE_PROGRAM_ID, - None, - ) - .await - .expect("Failed to create guardian signatures"); - - println!("Solver USDC balance: {:?}", usdc_balance); - let solver_token_account_address = solver.token_account_address().unwrap(); - let solver_token_account_info = test_context - .banks_client - .get_account(solver_token_account_address) - .await - .expect("Failed to query banks client for solver token account info") - .expect("Failed to get solver token account info"); - let solver_token_account = - TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); - assert!(solver_token_account.delegate.is_some()); -} - -#[tokio::test] -// Testing a initial offer from arbitrum to ethereum -// TODO: Make a test that checks that the auction account and maybe some other accounts are exactly the same as when using the fallback instruction -pub async fn test_place_initial_offer_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let testing_engine = TestingEngine::new(testing_context).await; - - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig { - solver_index: 1, - ..ImproveOfferInstructionConfig::default() - }), - ]; - - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_shim_blocks_non_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { - solver_index: 0, - ..PlaceInitialOfferInstructionConfig::default() - }), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { - solver_index: 1, - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PlaceInitialOfferInstructionConfig::default() - }), - ]; - - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_place_initial_offer_non_shim_blocks_shim() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { - solver_index: 0, - ..PlaceInitialOfferInstructionConfig::default() - }), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { - solver_index: 1, - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PlaceInitialOfferInstructionConfig::default() - }), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -// Testing an execute order from arbitrum to ethereum -// TODO: Flesh out this test to see if the message was posted correctly -pub async fn test_execute_order_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_execute_order_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_execute_order_fallback_blocks_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 3012, - error_string: "AccountNotInitialized".to_string(), - }), - ..ExecuteOrderInstructionConfig::default() - }), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -// From ethereum to arbitrum -#[tokio::test] -pub async fn test_prepare_order_shim_fallback() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -// Prepare order response from ethereum to arbitrum (shimless) -#[tokio::test] -pub async fn test_prepare_order_shimless() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_prepare_order_response_shimful_blocks_shimless() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - //TODO: This does not currently work, but the logs are as expected, I just don't know how to capture and test them - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { - // expected_log_message: Some("Already prepared".to_string()), - ..PrepareOrderInstructionConfig::default() - }), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_prepare_order_response_shimless_blocks_shimful() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), - // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { - expected_error: Some(ExpectedError { - instruction_index: 0, - error_code: 0, - error_string: TransactionError::AccountInUse.to_string(), - }), - ..PrepareOrderInstructionConfig::default() - }), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} - -#[tokio::test] -pub async fn test_settle_auction_complete() { - let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), - ]; - testing_engine - .execute(&mut test_context, instruction_triggers) - .await; -} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs new file mode 100644 index 000000000..87ca79949 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs @@ -0,0 +1,251 @@ +//! # Initialise and misc instruction testing +//! +//! This module contains tests for the initialise and some other miscellaneous setup test scenarios. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! +//! - `test_initialize_program` - Test that the program is initialised correctly +//! - `test_cctp_token_router_endpoint_creation` - Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains +//! - `test_local_token_router_endpoint_creation` - Test that a local token router endpoint is created for the arbitrum and ethereum chains +//! - `test_setup_vaas` - Test that the vaas are setup correctly +//! - `test_post_message_shims` - Test that the post message shims work correctly +//! - `test_approve_usdc` - Test that the approve usdc helper function works correctly + +use crate::shimful; +use crate::shimless; +use crate::testing_engine; +use crate::testing_engine::config::InitializeInstructionConfig; +use crate::utils; +use anchor_lang::AccountDeserialize; +use anchor_spl::token::TokenAccount; +use matching_engine::ID as PROGRAM_ID; +use shimful::post_message::set_up_post_message_transaction_test; +use shimless::initialize::{initialize_program, AuctionParametersConfig}; +use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::router::add_local_router_endpoint_ix; +use utils::vaa::VaaArgs; +use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; + +/* + Happy path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** * * ** +** ** ** ** + ** **** **** ** + ** ** ** ** + ** *** *** ** + *** **** **** *** + ** ****** ****** ** + *** *************** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + +/// Test that the program is initialised correctly +#[tokio::test] +pub async fn test_initialize_program() { + let (testing_context, mut test_context) = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, // Vaa args for creating vaas + ) + .await; + + let initialize_config = InitializeInstructionConfig::default(); + + let testing_engine = TestingEngine::new(testing_context).await; + + testing_engine + .execute( + &mut test_context, + vec![InstructionTrigger::InitializeProgram(initialize_config)], + ) + .await; +} + +/// Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains +#[tokio::test] +pub async fn test_cctp_token_router_endpoint_creation() { + let (testing_context, mut test_context) = setup_environment( + ShimMode::None, // Shim mode + TransferDirection::FromArbitrumToEthereum, // Transfer direction + None, // Vaa args + ) + .await; + + let initialize_config = InitializeInstructionConfig::default(); + + let testing_engine = TestingEngine::new(testing_context).await; + + testing_engine + .execute( + &mut test_context, + vec![InstructionTrigger::InitializeProgram(initialize_config)], + ) + .await; +} + +/// Test that a local token router endpoint is created for the arbitrum and ethereum chains +#[tokio::test] +pub async fn test_local_token_router_endpoint_creation() { + let (testing_context, mut test_context) = setup_environment( + ShimMode::None, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; + + let initialize_fixture = initialize_program( + &testing_context, + &mut test_context, + AuctionParametersConfig::default(), + None, // No expected error + None, // No expected log messages + ) + .await + .expect("Failed to initialize program"); + let payer_signer = testing_context.testing_actors.owner.keypair(); + let _local_token_router_endpoint = add_local_router_endpoint_ix( + &testing_context, + &mut test_context, + &payer_signer, + testing_context.testing_actors.owner.pubkey(), + initialize_fixture.get_custodian_address(), + testing_context.testing_actors.owner.keypair().as_ref(), + ) + .await; +} + +/// Test setting up vaas +/// Vaa is from arbitrum to ethereum +#[tokio::test] +pub async fn test_setup_vaas() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifySignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + testing_context.verify_vaas(&mut test_context).await; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute( + &mut test_context, + vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ], + ) + .await; +} + +/// Test that the post message shims work correctly +#[tokio::test] +pub async fn test_post_message_shims() { + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + None, + ) + .await; + let actors = &testing_context.testing_actors; + let emitter_signer = actors.owner.keypair(); + let payer_signer = actors.solvers[0].keypair(); + set_up_post_message_transaction_test( + &testing_context, + &mut test_context, + &payer_signer, + &emitter_signer, + ) + .await; +} + +/// Test that the approve usdc helper function works correctly +#[tokio::test] +pub async fn test_approve_usdc() { + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let first_test_ft = testing_context.get_vaa_pair(0).unwrap().fast_transfer_vaa; + let vaa_data = first_test_ft.vaa_data; + + let actors = &testing_context.testing_actors; + let solver = actors.solvers[0].clone(); + let offer_price: u64 = 1__000_000; + let program_id = PROGRAM_ID; + let new_pubkey = Pubkey::new_unique(); + + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &new_pubkey.to_bytes(), + &offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + solver + .approve_usdc(&mut test_context, &transfer_authority, offer_price) + .await; + + let usdc_balance = solver.get_token_account_balance(&mut test_context).await; + + // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. + let _guardian_signature_info = shimful::verify_shim::create_guardian_signatures( + &testing_context, + &mut test_context, + &actors.owner.keypair(), + &vaa_data, + &CORE_BRIDGE_PROGRAM_ID, + None, + ) + .await + .expect("Failed to create guardian signatures"); + + println!("Solver USDC balance: {:?}", usdc_balance); + let solver_token_account_address = solver.token_account_address().unwrap(); + let solver_token_account_info = test_context + .banks_client + .get_account(solver_token_account_address) + .await + .expect("Failed to query banks client for solver token account info") + .expect("Failed to get solver token account info"); + let solver_token_account = + TokenAccount::try_deserialize(&mut solver_token_account_info.data.as_ref()).unwrap(); + assert!(solver_token_account.delegate.is_some()); +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs new file mode 100644 index 000000000..cd214de97 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -0,0 +1,312 @@ +#![allow(clippy::expect_used)] +#![allow(clippy::panic)] + +//! # Make offer instruction testing +//! +//! This module contains tests for the place initial offer and improve offer instructions. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! +//! - `test_place_initial_offer_fallback` - Test that the place initial offer fallback instruction works correctly +//! +//! ### Sad path tests +//! +//! - `test_place_initial_offer_fails_if_fast_market_order_not_created` - Test that the place initial offer fails if the fast market order is not created +//! +use crate::testing_engine; +use crate::testing_engine::config::{ + InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, +}; +use crate::testing_engine::state::TestingEngineState; +use crate::utils; +use crate::utils::auction::compare_auctions; + +use anchor_lang::error::ErrorCode; +use anchor_lang::AccountDeserialize; +use matching_engine::state::Auction; +use solana_program_test::{tokio, ProgramTestContext}; +use solana_sdk::transaction::TransactionError; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::vaa::VaaArgs; + +/* + Happy path tests +*/ + +/// Test that the place initial offer fallback instruction works correctly from arbitrum to ethereum +#[tokio::test] +pub async fn test_place_initial_offer_fallback() { + let config = PlaceInitialOfferInstructionConfig::default(); + let (final_state, _) = place_initial_offer_fallback(config).await; + assert_eq!( + final_state + .fast_market_order() + .unwrap() + .fast_market_order + .digest(), + final_state + .base() + .vaas + .first() + .unwrap() + .fast_transfer_vaa + .get_vaa_data() + .digest() + ); +} + +/// Test that the place initial offer instruction works correctly without the shim instructions +#[tokio::test] +pub async fn test_place_initial_offer_shimless() { + let config = PlaceInitialOfferInstructionConfig::default(); + let (_final_state, _) = place_initial_offer_shimless(config).await; +} + +/// Test that auction account is exactly the same when using shimless and fallback instructions +#[tokio::test] +pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { + let config = PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Owner, + ..PlaceInitialOfferInstructionConfig::default() + }; + let (final_state_shimless, mut shimless_test_context) = + place_initial_offer_shimless(config.clone()).await; + let (final_state_fallback, mut fallback_test_context) = + place_initial_offer_fallback(config.clone()).await; + + let shimless_auction = { + let shimless_active_auction_address = final_state_shimless + .auction_state() + .get_active_auction() + .unwrap() + .auction_address; + let shimless_auction_account_data = shimless_test_context + .banks_client + .get_account(shimless_active_auction_address) + .await + .unwrap() + .unwrap() + .data; + Auction::try_deserialize(&mut &shimless_auction_account_data[..]).unwrap() + }; + let shimful_auction = { + let shimful_active_auction_address = final_state_fallback + .auction_state() + .get_active_auction() + .unwrap() + .auction_address; + let shimful_account_data = fallback_test_context + .banks_client + .get_account(shimful_active_auction_address) + .await + .unwrap() + .unwrap() + .data; + Auction::try_deserialize(&mut &shimful_account_data[..]).unwrap() + }; + compare_auctions(&shimless_auction, &shimful_auction).await; +} + +pub async fn place_initial_offer_fallback( + config: PlaceInitialOfferInstructionConfig, +) -> (TestingEngineState, ProgramTestContext) { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(config), + ]; + + ( + testing_engine + .execute(&mut test_context, instruction_triggers) + .await, + test_context, + ) +} + +pub async fn place_initial_offer_shimless( + config: PlaceInitialOfferInstructionConfig, +) -> (TestingEngineState, ProgramTestContext) { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(config), + ]; + ( + testing_engine + .execute(&mut test_context, instruction_triggers) + .await, + test_context, + ) +} + +/* + Sad path tests +*/ + +/// Test that the shimless place initial offer instruction blocks the shim instruction +#[tokio::test] +pub async fn test_place_initial_offer_non_shim_blocks_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Solver(0), + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Solver(1), + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the place initial offer shim blocks the non shim instruction +#[tokio::test] +pub async fn test_place_initial_offer_shim_blocks_non_shim() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Solver(0), + ..PlaceInitialOfferInstructionConfig::default() + }), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Solver(1), + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the place initial offer fails if the fast market order is not created +#[tokio::test] +pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let fake_fast_market_order_address = testing_context + .get_vaa_pair(0) + .unwrap() + .fast_transfer_vaa + .vaa_pubkey; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { + fast_market_order_address: OverwriteCurrentState::Some(fake_fast_market_order_address), + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(ErrorCode::ConstraintOwner), + error_string: "Fast market order account owner is invalid".to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }), + ]; + + let testing_engine = TestingEngine::new(testing_context).await; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs index b725a1f6d..b4b1f1ea4 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs @@ -1 +1,6 @@ -pub mod happy_path; +pub mod create_and_close_fast_market_order; +pub mod execute_order; +pub mod initialise_and_misc; +pub mod make_offer; +pub mod prepare_order; +pub mod settle_auction; diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs new file mode 100644 index 000000000..1d9b9e307 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -0,0 +1,175 @@ +#![allow(clippy::expect_used)] +#![allow(clippy::panic)] + +//! # Make offer instruction testing +//! +//! This module contains tests for the place initial offer and improve offer instructions. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! +//! - `test_prepare_order_shim_fallback` - Test that the prepare order shim fallback instruction works correctly +//! - `test_prepare_order_shimless` - Test that the prepare order shimless instruction works correctly +//! + +use crate::testing_engine; +use crate::testing_engine::config::{ + InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, PrepareOrderInstructionConfig, +}; +use crate::utils; + +use solana_program_test::tokio; +use solana_sdk::transaction::TransactionError; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::vaa::VaaArgs; + +/// Test that the prepare order fallback instruction works correctly (from ethereum to arbitrum) +#[tokio::test] +pub async fn test_prepare_order_shim_fallback() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the prepare order shimless instruction works correctly (from ethereum to arbitrum) +#[tokio::test] +pub async fn test_prepare_order_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/* + Sad path tests +*/ + +/// Test that the prepare order response shimless instruction blocks the shimful instruction +#[tokio::test] +pub async fn test_prepare_order_response_shimless_blocks_shimful() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: TransactionError::AccountInUse.to_string(), + }), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} + +/// Test that the prepare order response shimful instruction blocks the shimless instruction +#[tokio::test] +pub async fn test_prepare_order_response_shimful_blocks_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { + expected_log_messages: Some(vec![ExpectedLog { + log_message: "Already prepared".to_string(), + count: 1, + }]), + ..PrepareOrderInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs new file mode 100644 index 000000000..77e24f5aa --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -0,0 +1,55 @@ +//! # Settle auction instruction testing +//! +//! This module contains tests for the settle auction instruction. +//! +//! ## Test Cases +//! +//! ### Happy path tests +//! +//! - `test_settle_auction_complete` - Test that the settle auction instruction works correctly +//! +use crate::testing_engine; +use crate::testing_engine::config::{ + InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, +}; +use crate::utils; + +use solana_program_test::tokio; +use testing_engine::config::*; +use testing_engine::engine::{InstructionTrigger, TestingEngine}; +use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; +use utils::vaa::VaaArgs; + +/// Test that the settle auction instruction works correctly +#[tokio::test] +pub async fn test_settle_auction_complete() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index a2d232270..0e783e298 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -1,12 +1,31 @@ +//! # Testing Engine Config +//! +//! This module contains the configuration arguments for the testing engine. +//! +//! ## Examples +//! +//! ``` +//! use crate::testing_engine::config::*; +//! +//! let initialize_instruction_config = InitializeInstructionConfig::default(); +//! +//! let instruction_triggers = vec![ +//! InstructionTrigger::InitializeProgram(initialize_instruction_config), +//! ]; +//! ``` + use std::{collections::HashSet, rc::Rc}; use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; use anchor_lang::prelude::*; use solana_sdk::signature::Keypair; +use super::setup::{TestingActor, TestingActors}; + /// An instruction config contains the configuration arguments for an instruction as well as the expected error pub trait InstructionConfig: Default { fn expected_error(&self) -> Option<&ExpectedError>; + fn expected_log_messages(&self) -> Option<&Vec>; } /// A type alias for an optional value that overwrites the current state @@ -18,7 +37,7 @@ pub type OverwriteCurrentState = Option; /// /// * `instruction_index` - The index of the instruction that is expected to error /// * `error_code` - The error code that is expected to be returned -/// * `error_string` - The error string that is expected to be returned +/// * `error_string` - A description of the error that is expected to be returned for debugging purposes #[derive(Clone)] pub struct ExpectedError { pub instruction_index: u8, @@ -42,18 +61,23 @@ pub struct ExpectedLog { pub struct InitializeInstructionConfig { pub auction_parameters_config: AuctionParametersConfig, pub expected_error: Option, + pub expected_log_messages: Option>, } impl InstructionConfig for InitializeInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } pub struct CreateCctpRouterEndpointsInstructionConfig { pub chains: HashSet, pub payer_signer: Option>, pub admin_owner_or_assistant: Option>, pub expected_error: Option, + pub expected_log_messages: Option>, } impl Default for CreateCctpRouterEndpointsInstructionConfig { @@ -63,6 +87,7 @@ impl Default for CreateCctpRouterEndpointsInstructionConfig { payer_signer: None, admin_owner_or_assistant: None, expected_error: None, + expected_log_messages: None, } } } @@ -71,6 +96,9 @@ impl InstructionConfig for CreateCctpRouterEndpointsInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } #[derive(Clone, Default)] @@ -78,13 +106,17 @@ pub struct InitializeFastMarketOrderShimInstructionConfig { pub fast_market_order_id: u32, pub close_account_refund_recipient: Option, // If none defaults to solver 0 pubkey, pub payer_signer: Option>, // If none defaults to owner keypair - pub expected_error: Option, + pub expected_error: Option, // If none, will not check for an error + pub expected_log_messages: Option>, // If none, will not check for logs } impl InstructionConfig for InitializeFastMarketOrderShimInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } #[derive(Clone, Default)] @@ -100,6 +132,9 @@ impl InstructionConfig for PrepareOrderInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } #[derive(Clone, Default)] @@ -108,24 +143,32 @@ pub struct ExecuteOrderInstructionConfig { pub solver_index: usize, pub payer_signer: Option>, pub expected_error: Option, + pub expected_log_messages: Option>, } impl InstructionConfig for ExecuteOrderInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } #[derive(Clone, Default)] pub struct SettleAuctionInstructionConfig { pub payer_signer: Option>, pub expected_error: Option, + pub expected_log_messages: Option>, } impl InstructionConfig for SettleAuctionInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } #[derive(Clone, Default)] @@ -133,30 +176,58 @@ pub struct CloseFastMarketOrderShimInstructionConfig { pub close_account_refund_recipient_keypair: Option>, // If none, will use the solver 0 keypair pub fast_market_order_address: OverwriteCurrentState, // If none, will use the fast market order address from the current state pub expected_error: Option, + pub expected_log_messages: Option>, } impl InstructionConfig for CloseFastMarketOrderShimInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } +#[derive(Clone)] +pub enum TestingActorEnum { + Solver(usize), + Owner, +} + +impl TestingActorEnum { + pub fn get_actor(&self, testing_actors: &TestingActors) -> TestingActor { + match self { + Self::Solver(index) => testing_actors.solvers[*index].actor.clone(), + Self::Owner => testing_actors.owner.clone(), + } + } +} + +impl Default for TestingActorEnum { + fn default() -> Self { + Self::Solver(0) + } +} + +#[derive(Clone)] pub struct PlaceInitialOfferInstructionConfig { - pub solver_index: usize, + pub actor: TestingActorEnum, pub offer_price: u64, pub payer_signer: Option>, pub fast_market_order_address: OverwriteCurrentState, pub expected_error: Option, + pub expected_log_messages: Option>, } impl Default for PlaceInitialOfferInstructionConfig { fn default() -> Self { Self { - solver_index: 0, + actor: TestingActorEnum::Solver(0), offer_price: 1__000_000, payer_signer: None, fast_market_order_address: None, expected_error: None, + expected_log_messages: None, } } } @@ -165,6 +236,9 @@ impl InstructionConfig for PlaceInitialOfferInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } pub struct ImproveOfferInstructionConfig { @@ -172,6 +246,7 @@ pub struct ImproveOfferInstructionConfig { pub offer_price: u64, pub payer_signer: Option>, pub expected_error: Option, + pub expected_log_messages: Option>, } impl Default for ImproveOfferInstructionConfig { @@ -181,6 +256,7 @@ impl Default for ImproveOfferInstructionConfig { offer_price: 500_000, payer_signer: None, expected_error: None, + expected_log_messages: None, } } } @@ -189,4 +265,7 @@ impl InstructionConfig for ImproveOfferInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 8a04eb5f9..21d72b768 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -1,3 +1,27 @@ +//! # Testing Engine +//! +//! This module contains the testing engine for the matching engine program. +//! It is used to test the matching engine program with a functional style. +//! +//! ## Features +//! +//! - Testing engine struct (TestingEngine struct) +//! - Execute instructions (impl TestingEngine) +//! - Fast forward slots (fn fast_forward_slots) +//! +//! ## Examples +//! +//! ``` +//! use crate::testing_engine::engine::*; +//! +//! let testing_context = setup_testing_context(//arguments); +//! let testing_engine = TestingEngine::new(testing_context).await; +//! let instruction_triggers = vec![ +//! InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), +//! ]; +//! testing_engine.execute(instruction_triggers).await; +//! ``` + use matching_engine::state::FastMarketOrder; use solana_program_test::ProgramTestContext; use solana_sdk::signer::Signer; @@ -11,6 +35,7 @@ use crate::shimful::fast_market_order_shim::{ }; use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; +use crate::testing_engine::setup::ShimMode; use crate::utils::auction::AuctionState; use crate::utils::vaa::TestVaaPairs; use crate::utils::{ @@ -38,6 +63,18 @@ pub enum InstructionTrigger { CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } +impl InstructionTrigger { + pub fn is_shim(&self) -> bool { + matches!( + self, + Self::PlaceInitialOfferShim(_) + | Self::ExecuteOrderShim(_) + | Self::PrepareOrderShim(_) + | Self::InitializeFastMarketOrderShim(_) + | Self::CloseFastMarketOrderShim(_) + ) + } +} // Implement InstructionConfig for InstructionTrigger impl InstructionConfig for InstructionTrigger { fn expected_error(&self) -> Option<&ExpectedError> { @@ -56,6 +93,22 @@ impl InstructionConfig for InstructionTrigger { Self::CloseFastMarketOrderShim(config) => config.expected_error(), } } + fn expected_log_messages(&self) -> Option<&Vec> { + match self { + Self::InitializeProgram(config) => config.expected_log_messages(), + Self::CreateCctpRouterEndpoints(config) => config.expected_log_messages(), + Self::InitializeFastMarketOrderShim(config) => config.expected_log_messages(), + Self::PlaceInitialOfferShimless(config) => config.expected_log_messages(), + Self::PlaceInitialOfferShim(config) => config.expected_log_messages(), + Self::ImproveOfferShimless(config) => config.expected_log_messages(), + Self::ExecuteOrderShim(config) => config.expected_log_messages(), + Self::ExecuteOrderShimless(config) => config.expected_log_messages(), + Self::PrepareOrderShim(config) => config.expected_log_messages(), + Self::PrepareOrderShimless(config) => config.expected_log_messages(), + Self::SettleAuction(config) => config.expected_log_messages(), + Self::CloseFastMarketOrderShim(config) => config.expected_log_messages(), + } + } } // If you need a default implementation @@ -111,20 +164,48 @@ impl TestingEngine { Self { testing_context } } + /// Executes a chain of instruction triggers + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `instruction_chain` - The chain of instruction triggers to execute pub async fn execute( &self, test_context: &mut ProgramTestContext, instruction_chain: Vec, - ) { + ) -> TestingEngineState { let mut current_state = self.create_initial_state(); + self.verify_triggers(&instruction_chain); + for trigger in instruction_chain { current_state = self .execute_trigger(test_context, ¤t_state, &trigger) .await; } + current_state + } + + /// Verifies that the shimmode corresponds to the instruction chain + fn verify_triggers(&self, instruction_chain: &[InstructionTrigger]) { + // If any shim instructions are present, make sure that shim mode is set to VerifyAndPostSignature + if instruction_chain.iter().any(|trigger| trigger.is_shim()) { + assert_eq!( + self.testing_context.shim_mode, + ShimMode::VerifyAndPostSignature, + "Shim mode is not set to VerifyAndPostSignature, and a shim instruction trigger is present" + ); + } } + /// Executes an instruction trigger and returns the updated testing engine state + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `current_state` - The current state of the testing engine + /// * `trigger` - The instruction trigger to execute async fn execute_trigger( &self, test_context: &mut ProgramTestContext, @@ -183,6 +264,7 @@ impl TestingEngine { } } + /// Creates the initial state for the testing engine pub fn create_initial_state(&self) -> TestingEngineState { let fixture_accounts = self .testing_context @@ -198,6 +280,7 @@ impl TestingEngine { }) } + /// Instruction trigger function for initializing the program async fn initialize_program( &self, test_context: &mut ProgramTestContext, @@ -206,6 +289,7 @@ impl TestingEngine { ) -> TestingEngineState { let auction_parameters_config = config.auction_parameters_config.clone(); let expected_error = config.expected_error(); + let expected_log_messages = config.expected_log_messages(); let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { let result = shimless::initialize::initialize_program( @@ -213,6 +297,7 @@ impl TestingEngine { test_context, auction_parameters_config, expected_error, + expected_log_messages, ) .await; @@ -248,6 +333,7 @@ impl TestingEngine { initial_state.clone() } + /// Instruction trigger function for creating cctp router endpoints async fn create_cctp_router_endpoints( &self, test_context: &mut ProgramTestContext, @@ -284,6 +370,7 @@ impl TestingEngine { } } + /// Instruction trigger function for creating a fast market order account async fn create_fast_market_order_account( &self, test_context: &mut ProgramTestContext, @@ -352,6 +439,7 @@ impl TestingEngine { } } + /// Instruction trigger function for closing a fast market order account async fn close_fast_market_order_account( &self, test_context: &mut ProgramTestContext, @@ -403,12 +491,7 @@ impl TestingEngine { .payer_signer .clone() .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); - let solver = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index"); + let solver = config.actor.get_actor(&self.testing_context.testing_actors); let expected_error = config.expected_error(); let fast_vaa = ¤t_state .base() @@ -466,6 +549,7 @@ impl TestingEngine { current_state.clone() } + /// Instruction trigger function for improving an offer async fn improve_offer_shimless( &self, test_context: &mut ProgramTestContext, @@ -495,19 +579,19 @@ impl TestingEngine { ) .await; if expected_error.is_none() { - let auction_state = new_auction_state.unwrap(); return TestingEngineState::OfferImproved { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), router_endpoints: current_state.router_endpoints().unwrap().clone(), fast_market_order: current_state.fast_market_order().cloned(), - auction_state, + auction_state: new_auction_state, auction_accounts: current_state.auction_accounts().cloned(), }; } current_state.clone() } + /// Instruction trigger function for placing an initial offer async fn place_initial_offer_shim( &self, test_context: &mut ProgramTestContext, @@ -523,7 +607,7 @@ impl TestingEngine { let router_endpoints = current_state .router_endpoints() .expect("Router endpoints are not created"); - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let solver = config.actor.get_actor(&self.testing_context.testing_actors); let payer_signer = config .payer_signer .clone() @@ -582,6 +666,7 @@ impl TestingEngine { current_state.clone() } + /// Instruction trigger function for executing an order async fn execute_order_shim( &self, test_context: &mut ProgramTestContext, @@ -635,6 +720,7 @@ impl TestingEngine { } } + /// Instruction trigger function for executing an order async fn execute_order_shimless( &self, test_context: &mut ProgramTestContext, @@ -662,7 +748,7 @@ impl TestingEngine { .fast_transfer_vaa .get_vaa_pubkey(), ), - solver.clone(), + solver.actor.clone(), auction_config_address, &router_endpoints.endpoints, custodian_address, @@ -699,6 +785,7 @@ impl TestingEngine { } } + /// Instruction trigger function for preparing an order async fn prepare_order_shim( &self, test_context: &mut ProgramTestContext, @@ -755,6 +842,7 @@ impl TestingEngine { } } + /// Instruction trigger function for preparing an order async fn prepare_order_shimless( &self, test_context: &mut ProgramTestContext, @@ -808,6 +896,7 @@ impl TestingEngine { } } + /// Instruction trigger function for settling an auction async fn settle_auction( &self, test_context: &mut ProgramTestContext, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index d591385b9..b5c720f21 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -1,3 +1,17 @@ +//! # Testing Engine Setup +//! +//! This module contains the setup for the testing engine. +//! It is used to create the pre-testing context and the testing context. +//! +//! ## Examples +//! +//! ``` +//! use crate::testing_engine::setup::*; +//! +//! let testing_context = setup_testing_context(//arguments); +//! let testing_engine = TestingEngine::new(testing_context).await; +//! ``` + use crate::testing_engine::config::{ExpectedError, ExpectedLog}; use crate::utils::account_fixtures::FixtureAccounts; use crate::utils::airdrop::airdrop; @@ -144,6 +158,7 @@ pub struct TestingContext { pub fixture_accounts: Option, pub vaa_pairs: TestVaaPairs, pub transfer_direction: TransferDirection, + pub shim_mode: ShimMode, } impl TestingContext { @@ -162,6 +177,7 @@ impl TestingContext { mut pre_testing_context: PreTestingContext, transfer_direction: TransferDirection, vaas_test: Option, + shim_mode: ShimMode, ) -> (Self, ProgramTestContext) { let mut test_context = pre_testing_context.program_test.start_with_context().await; @@ -190,6 +206,7 @@ impl TestingContext { fixture_accounts: Some(pre_testing_context.account_fixtures), vaa_pairs, transfer_direction, + shim_mode, }, test_context, ) @@ -301,8 +318,6 @@ impl TestingContext { /// # Returns /// /// The simulation details if the transaction was successful and all expected logs were found - #[allow(dead_code)] - // TODO: Use this pub async fn simulate_and_verify_logs( &self, test_context: &mut ProgramTestContext, @@ -313,7 +328,6 @@ impl TestingContext { .banks_client .simulate_transaction(transaction) .await?; - // Verify the transaction succeeded assert!( simulation_result.result.clone().unwrap().is_ok(), @@ -419,41 +433,30 @@ impl Solver { self.actor.token_account.as_ref().map(|t| t.address) } + /// Approves the USDC mint for the given delegate + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `delegate` - The delegate to approve the USDC mint to + /// * `amount` - The amount of USDC to approve pub async fn approve_usdc( &self, test_context: &mut ProgramTestContext, delegate: &Pubkey, amount: u64, ) { - // If signer pubkeys are empty, it means that the owner is the signer - let last_blockhash = test_context - .get_new_latest_blockhash() - .await - .expect("Failed to get new blockhash"); - let approve_ix = approve( - &spl_token::ID, - &self.token_account_address().unwrap(), - delegate, - &self.actor.pubkey(), - &[], - amount, - ) - .expect("Failed to create approve USDC instruction"); - let transaction = Transaction::new_signed_with_payer( - &[approve_ix], - Some(&self.actor.pubkey()), - &[&self.actor.keypair()], - last_blockhash, - ); - test_context - .banks_client - .process_transaction(transaction) - .await - .expect("Failed to approve USDC"); + self.actor + .approve_usdc(test_context, delegate, amount) + .await; + } + + pub async fn get_token_account_balance(&self, test_context: &mut ProgramTestContext) -> u64 { + self.actor.get_token_account_balance(test_context).await } - pub async fn get_balance(&self, test_context: &mut ProgramTestContext) -> u64 { - self.actor.get_balance(test_context).await + pub async fn get_lamport_balance(&self, test_context: &mut ProgramTestContext) -> u64 { + self.actor.get_lamport_balance(test_context).await } } @@ -503,7 +506,7 @@ impl TestingActor { /// # Arguments /// /// * `test_context` - The test context - pub async fn get_balance(&self, test_context: &mut ProgramTestContext) -> u64 { + pub async fn get_token_account_balance(&self, test_context: &mut ProgramTestContext) -> u64 { if let Some(token_account) = self.token_account_address() { let account = test_context .banks_client @@ -517,6 +520,54 @@ impl TestingActor { 0 } } + + pub async fn get_lamport_balance(&self, test_context: &mut ProgramTestContext) -> u64 { + test_context + .banks_client + .get_balance(self.keypair.pubkey()) + .await + .unwrap() + } + + /// Approves the USDC mint for the given delegate + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `delegate` - The delegate to approve the USDC mint to + /// * `amount` - The amount of USDC to approve + pub async fn approve_usdc( + &self, + test_context: &mut ProgramTestContext, + delegate: &Pubkey, + amount: u64, + ) { + // If signer pubkeys are empty, it means that the owner is the signer + let last_blockhash = test_context + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let approve_ix = approve( + &spl_token::ID, + &self.token_account_address().unwrap(), + delegate, + &self.pubkey(), + &[], + amount, + ) + .expect("Failed to create approve USDC instruction"); + let transaction = Transaction::new_signed_with_payer( + &[approve_ix], + Some(&self.pubkey()), + &[&self.keypair()], + last_blockhash, + ); + test_context + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to approve USDC"); + } } /// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) @@ -557,6 +608,7 @@ impl TestingActors { pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { let mut actors = Vec::new(); actors.push(&mut self.fee_recipient); + actors.push(&mut self.owner); for solver in &mut self.solvers { actors.push(&mut solver.actor); } @@ -616,6 +668,7 @@ impl TestingActors { /// * `PostVaa` - Post the VAAs but don't add the shims /// * `VerifySignature` - Only add the verify signature shim program /// * `VerifyAndPostSignature` - Add the verify signature and post message shims program +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ShimMode { None, VerifySignature, @@ -623,7 +676,7 @@ pub enum ShimMode { } #[allow(dead_code)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum TransferDirection { FromArbitrumToEthereum, FromEthereumToArbitrum, @@ -715,5 +768,11 @@ pub async fn setup_environment( pre_testing_context.add_post_message_shims(); } }; - TestingContext::new(pre_testing_context, transfer_direction, vaas_test).await + TestingContext::new( + pre_testing_context, + transfer_direction, + vaas_test, + shim_mode, + ) + .await } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 45dcf0061..36d86c2fc 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -1,3 +1,17 @@ +//! # Testing Engine State +//! +//! This module contains the state for the testing engine. +//! It is used to store the state of the testing engine. +//! +//! ## Examples +//! +//! ``` +//! use crate::testing_engine::state::*; +//! +//! let testing_engine_state = TestingEngineState::Uninitialized(BaseState::default()); +//! // Use the testing engine state to test the instructions and move through the states +//! ``` + use super::setup::TransferDirection; use crate::utils::{ account_fixtures::FixtureAccounts, diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index ec8b6fc29..d3a78f26c 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -3,14 +3,14 @@ use solana_program_test::ProgramTestContext; use super::router::TestRouterEndpoints; use super::Chain; -use crate::testing_engine::setup::{Solver, TestingContext, TransferDirection}; +use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; use anyhow::{anyhow, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; #[derive(Clone)] pub struct AuctionAccounts { pub posted_fast_vaa: Option, pub offer_token: Pubkey, - pub solver: Solver, + pub actor: TestingActor, pub auction_config: Pubkey, pub from_router_endpoint: Pubkey, pub to_router_endpoint: Pubkey, @@ -18,7 +18,13 @@ pub struct AuctionAccounts { pub usdc_mint: Pubkey, } -#[allow(dead_code)] +/// An enum representing the state of an auction +/// +/// # Fields +/// +/// * `Active` - The auction is active +/// * `Settled` - The auction is settled +/// * `Inactive` - The auction is inactive #[derive(Clone)] pub enum AuctionState { Active(Box), @@ -35,6 +41,16 @@ impl AuctionState { } } } + +/// A struct representing an active auction +/// +/// # Fields +/// +/// * `auction_address` - The address of the auction +/// * `auction_custody_token_address` - The address of the auction custody token +/// * `auction_config_address` - The address of the auction config +/// * `initial_offer` - The initial offer of the auction +/// * `best_offer` - The best offer of the auction #[derive(Clone)] pub struct ActiveAuctionState { pub auction_address: Pubkey, @@ -44,6 +60,13 @@ pub struct ActiveAuctionState { pub best_offer: AuctionOffer, } +/// A struct representing an auction offer +/// +/// # Fields +/// +/// * `participant` - The participant of the offer +/// * `offer_token` - The token of the offer +/// * `offer_price` - The price of the offer #[derive(Clone)] pub struct AuctionOffer { pub participant: Pubkey, @@ -54,7 +77,7 @@ pub struct AuctionOffer { impl AuctionAccounts { pub fn new( posted_fast_vaa: Option, - solver: Solver, + actor: TestingActor, auction_config: Pubkey, router_endpoints: &TestRouterEndpoints, custodian: Pubkey, @@ -80,8 +103,8 @@ impl AuctionAccounts { }; Self { posted_fast_vaa, - offer_token: solver.token_account_address().unwrap(), - solver, + offer_token: actor.token_account_address().unwrap(), + actor, auction_config, from_router_endpoint, to_router_endpoint, @@ -92,6 +115,16 @@ impl AuctionAccounts { } impl ActiveAuctionState { + /// Verifies the auction state against the expected auction state + /// + /// # Arguments + /// + /// * `testing_context` - The testing context + /// * `test_context` - The test context + /// + /// # Returns + /// + /// Result<()> - Panics if the auction state is not as expected or errors if the auction account is not found pub async fn verify_auction( &self, testing_context: &TestingContext, @@ -107,9 +140,9 @@ impl ActiveAuctionState { let auction_info = auction_account_data.info.unwrap(); let expected_auction_info = AuctionInfo { - config_id: 0, - custody_token_bump: 254, // TODO: Figure this out - vaa_sequence: 0, // No need to cehck against this + config_id: 0, // Not tested against + custody_token_bump: 254, // Not tested against + vaa_sequence: 0, // Not tested against source_chain: { match testing_context.transfer_direction { TransferDirection::FromEthereumToArbitrum => 3, @@ -118,15 +151,15 @@ impl ActiveAuctionState { return Err(anyhow!("Unsupported transfer direction")); } } - }, - best_offer_token: self.best_offer.offer_token, - initial_offer_token: self.initial_offer.offer_token, - start_slot: 1, - amount_in: 69000000, - security_deposit: 10545000, - offer_price: self.best_offer.offer_price, - redeemer_message_len: 0, - destination_asset_info: None, + }, // Tested against + best_offer_token: self.best_offer.offer_token, // Tested against + initial_offer_token: self.initial_offer.offer_token, // Tested against + start_slot: 1, // Not tested against + amount_in: 69000000, // Not tested against + security_deposit: 10545000, // Not tested against + offer_price: self.best_offer.offer_price, // Tested against + redeemer_message_len: 0, // Not tested against + destination_asset_info: None, // Not tested against }; assert_eq!(auction_info.config_id, expected_auction_info.config_id); @@ -134,9 +167,29 @@ impl ActiveAuctionState { assert_eq!(auction_info.offer_price, expected_auction_info.offer_price); assert_eq!( - auction_info.redeemer_message_len, - expected_auction_info.redeemer_message_len + auction_info.best_offer_token, + expected_auction_info.best_offer_token + ); + assert_eq!( + auction_info.initial_offer_token, + expected_auction_info.initial_offer_token ); Ok(()) } } + +pub async fn compare_auctions(auction_1: &Auction, auction_2: &Auction) { + let auction_1_info = auction_1.info.unwrap(); + let auction_2_info = auction_2.info.unwrap(); + assert_eq!(auction_1_info.config_id, auction_2_info.config_id); + assert_eq!( + auction_1_info.best_offer_token, + auction_2_info.best_offer_token + ); + assert_eq!( + auction_1_info.initial_offer_token, + auction_2_info.initial_offer_token + ); + assert_eq!(auction_1_info.start_slot, auction_2_info.start_slot); + assert_eq!(auction_1_info.offer_price, auction_2_info.offer_price); +} From bac17807a94db8a42c5e6887c7b194dc4678f3c2 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 2 Apr 2025 14:56:17 +0100 Subject: [PATCH 052/112] added filestrings to most places --- .../tests/testing_engine/setup.rs | 18 +++- .../tests/utils/account_fixtures.rs | 6 ++ .../tests/utils/airdrop.rs | 10 ++- .../tests/utils/auction.rs | 19 +++++ .../tests/utils/cctp_message.rs | 85 ++++++++++++++++++- .../tests/utils/constants.rs | 42 +++++++-- .../tests/utils/mint.rs | 4 + .../tests/utils/program_fixtures.rs | 6 ++ .../tests/utils/public_keys.rs | 5 ++ .../tests/utils/router.rs | 5 +- .../tests/utils/token_account.rs | 5 ++ .../tests/utils/vaa.rs | 5 ++ 12 files changed, 194 insertions(+), 16 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index b5c720f21..c06778915 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -581,13 +581,19 @@ pub struct TestingActors { } impl TestingActors { + /// Create a new TestingActors struct + /// + /// # Arguments + /// + /// * `owner_keypair_path` - The path to the owner keypair + /// + /// # Returns pub fn new(owner_keypair_path: &str) -> Self { let owner_kp = Rc::new(read_keypair_from_file(owner_keypair_path)); let owner = TestingActor::new(owner_kp.clone(), None); let owner_assistant = TestingActor::new(owner_kp.clone(), None); let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None); let relayer = TestingActor::new(Rc::new(Keypair::new()), None); - // TODO: Change player 1 solver to use the keyfile let mut solvers = vec![]; solvers.extend(vec![ Solver::new(Rc::new(Keypair::new()), None), @@ -605,6 +611,7 @@ impl TestingActors { } } + /// Get the actors that should have token accounts pub fn token_account_actors(&mut self) -> Vec<&mut TestingActor> { let mut actors = Vec::new(); actors.push(&mut self.fee_recipient); @@ -644,7 +651,7 @@ impl TestingActors { /// Add solvers to the testing actors #[allow(dead_code)] - async fn add_solvers( + pub async fn add_solvers( &mut self, test_context: &mut ProgramTestContext, num_solvers: usize, @@ -675,6 +682,13 @@ pub enum ShimMode { VerifyAndPostSignature, } +/// The direction of the transfer +/// +/// # Enums +/// +/// * `FromArbitrumToEthereum` - The direction of the transfer from Arbitrum to Ethereum +/// * `FromEthereumToArbitrum` - The direction of the transfer from Ethereum to Arbitrum +/// * `Other` - The direction of the transfer is not supported #[allow(dead_code)] #[derive(Copy, Clone, PartialEq, Eq)] pub enum TransferDirection { diff --git a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs index bc353d837..d1a19f181 100644 --- a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs @@ -1,3 +1,9 @@ +//! # Account Fixtures +//! +//! This module provides fixtures for creating accounts in the test environment. +//! It includes methods for creating accounts and for reading a keypair from a JSON fixture file. +//! These accounts are located in the `tests/fixtures/accounts` directory. + use anchor_lang::prelude::{pubkey, Pubkey}; use anyhow::Result as AnyhowResult; use serde_json::Value; diff --git a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs index 643522bfa..8d6612d63 100644 --- a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs +++ b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs @@ -9,10 +9,9 @@ use super::constants; /// /// # Arguments /// -/// * `test_context` - The test context +/// * `test_context` - The program test context /// * `recipient` - The recipient of the airdrop /// * `amount` - The amount of SOL to airdrop - pub async fn airdrop(test_context: &mut ProgramTestContext, recipient: &Pubkey, amount: u64) { // Create the transfer instruction with values from the context let transfer_ix = system_instruction::transfer(&test_context.payer.pubkey(), recipient, amount); @@ -32,6 +31,13 @@ pub async fn airdrop(test_context: &mut ProgramTestContext, recipient: &Pubkey, .unwrap(); } +/// Airdrops USDC to a given recipient +/// +/// # Arguments +/// +/// * `test_context` - The program test context +/// * `recipient_ata` - The recipient's ATA +/// * `amount` - The amount of USDC to airdrop pub async fn airdrop_usdc( test_context: &mut ProgramTestContext, recipient_ata: &Pubkey, diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index d3a78f26c..b50f0ff50 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -6,6 +6,19 @@ use super::Chain; use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; use anyhow::{anyhow, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; + +/// A struct representing the accounts for an auction +/// +/// # Fields +/// +/// * `posted_fast_vaa` - The address of the posted fast VAA +/// * `offer_token` - The address of the offer token +/// * `actor` - The actor of the auction (who places the initial offer, improves it, executes it, or settles it) +/// * `auction_config` - The address of the auction config +/// * `from_router_endpoint` - The address of the router endpoint for the source chain +/// * `to_router_endpoint` - The address of the router endpoint for the destination chain +/// * `custodian` - The address of the custodian +/// * `usdc_mint` - The usdc mint address #[derive(Clone)] pub struct AuctionAccounts { pub posted_fast_vaa: Option, @@ -178,6 +191,12 @@ impl ActiveAuctionState { } } +/// Compares two auctions to assert they are equal +/// +/// # Arguments +/// +/// * `auction_1` - The first auction +/// * `auction_2` - The second auction pub async fn compare_auctions(auction_1: &Auction, auction_2: &Auction) { let auction_1_info = auction_1.info.unwrap(); let auction_2_info = auction_2.info.unwrap(); diff --git a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs index 200f1e85f..25da65623 100644 --- a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs +++ b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs @@ -131,7 +131,7 @@ pub enum TokenMessengerError { InvalidTokenMint, } -//https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/state.rs#L35-L38 +// Imported from https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/state.rs#L35-L38 #[derive(Debug, InitSpace)] pub struct CctpRemoteTokenMessenger { pub domain: u32, // Big endian @@ -147,7 +147,7 @@ impl From<&RemoteTokenMessenger> for CctpRemoteTokenMessenger { } } -// https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/message-transmitter/src/message.rs#L30 +// Imported from https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/message-transmitter/src/message.rs#L30 #[derive(Clone, Debug)] pub struct Message<'a> { data: &'a [u8], @@ -296,7 +296,7 @@ impl<'a> Message<'a> { } } -// https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/burn_message.rs#L26 +// Imported from https://github.com/circlefin/solana-cctp-contracts/blob/4477f889732209dfc9a08b3aeaeb9203a324055c/programs/token-messenger-minter/src/token_messenger/burn_message.rs#L26 #[derive(Clone, Debug)] pub struct BurnMessage<'a> { data: &'a [u8], @@ -393,6 +393,15 @@ pub struct CircleAttester { } impl CircleAttester { + /// Creates an attestation for a given message + /// + /// # Arguments + /// + /// * `message` - The message to attest to + /// + /// # Returns + /// + /// A 65 byte array containing the attestation and the recovery id in the last byte pub fn create_attestation(&self, message: &[u8]) -> [u8; 65] { // Sign the message hash with the guardian key let secp = secp256k1::SECP256K1; @@ -425,6 +434,14 @@ impl Default for CircleAttester { } } +/// A struct representing a CCTP token burn message +/// +/// # Fields +/// +/// * `destination_cctp_domain` - The destination CCTP domain +/// * `cctp_message` - The CCTP message +/// * `encoded_cctp_burn_message` - The encoded CCTP burn message +/// * `cctp_attestation` - The CCTP attestation pub struct CctpTokenBurnMessage { pub destination_cctp_domain: u32, pub cctp_message: CctpMessage, @@ -440,6 +457,17 @@ impl CctpTokenBurnMessage { } } +/// A struct representing a CCTP message header +/// +/// # Fields +/// +/// * `version` - The version of the CCTP message +/// * `source_domain` - The source CCTP domain +/// * `destination_domain` - The destination CCTP domain +/// * `nonce` - The nonce of the CCTP message +/// * `sender` - The sender of the CCTP message +/// * `recipient` - The recipient of the CCTP message +/// * `destination_caller` - The destination caller of the CCTP message pub struct CctpMessageHeader { pub version: u32, pub source_domain: u32, @@ -470,6 +498,15 @@ impl CctpMessageHeader { } } +/// A struct representing a CCTP message body +/// +/// # Fields +/// +/// * `version` - The version of the CCTP message +/// * `burn_token_address` - The address of the token to burn +/// * `mint_recipient` - The address of the recipient of the token +/// * `amount` - The amount of the token to burn +/// * `message_sender` - The address of the sender of the message pub struct CctpMessageBody { pub version: u32, pub burn_token_address: [u8; 32], @@ -520,6 +557,12 @@ impl From<&BurnMessage<'_>> for CctpMessageBody { } } +/// A struct representing a CCTP message +/// +/// # Fields +/// +/// * `header` - The header of the CCTP message +/// * `body` - The body of the CCTP message pub struct CctpMessage { pub header: CctpMessageHeader, pub body: CctpMessageBody, @@ -535,6 +578,18 @@ impl CctpMessage { } } +/// Crafts a CCTP token burn message +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `source_cctp_domain` - The source CCTP domain +/// * `cctp_nonce` - The nonce of the CCTP message +/// * `amount` - The amount of the token to burn +/// * `message_transmitter_config_pubkey` - The pubkey of the message transmitter config +/// * `remote_token_messenger` - The remote token messenger +/// * `cctp_mint_recipient` - The address of the recipient of the token +/// * `custodian_address` - The address of the custodian #[allow(clippy::too_many_arguments)] pub async fn craft_cctp_token_burn_message( test_context: &mut ProgramTestContext, @@ -612,6 +667,15 @@ pub async fn craft_cctp_token_burn_message( }) } +/// Converts an Ethereum address to a wormhole universal address +/// +/// # Arguments +/// +/// * `eth_address` - The Ethereum address to convert +/// +/// # Returns +/// +/// A 32-byte array containing the universal address pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { // Remove '0x' prefix if present let address_str = eth_address @@ -629,6 +693,15 @@ pub fn ethereum_address_to_universal(eth_address: &str) -> [u8; 32] { universal_address } +/// Gets the base fee for a deposit +/// +/// # Arguments +/// +/// * `deposit` - The deposit to get the base fee for +/// +/// # Returns +/// +/// The base fee for the deposit pub fn get_deposit_base_fee(deposit: &Deposit) -> u64 { let payload = deposit.payload.clone(); let liquidity_layer_message = LiquidityLayerDepositMessage::parse(&payload).unwrap(); @@ -664,6 +737,12 @@ impl UsedNonces { } } +/// A struct representing a decoded CCTP message +/// +/// # Fields +/// +/// * `nonce` - The nonce of the CCTP message +/// * `source_domain` - The source CCTP domain #[derive(Debug)] pub struct CctpMessageDecoded { pub nonce: u64, diff --git a/solana/modules/matching-engine-testing/tests/utils/constants.rs b/solana/modules/matching-engine-testing/tests/utils/constants.rs index 687df52dc..076885674 100644 --- a/solana/modules/matching-engine-testing/tests/utils/constants.rs +++ b/solana/modules/matching-engine-testing/tests/utils/constants.rs @@ -1,5 +1,39 @@ #![allow(dead_code)] +//! # Constants +//! +//! This module contains constants for the matching engine testing module. +//! +//! ## Exposed constants +//! +//! - `CORE_BRIDGE_PID` - The program ID of the core bridge +//! - `CORE_BRIDGE_FEE_COLLECTOR` - The fee collector of the core bridge +//! - `CORE_BRIDGE_CONFIG` - The config of the core bridge +//! - `TOKEN_BRIDGE_PID` - The program ID of the token bridge +//! - `TOKEN_BRIDGE_EMITTER_AUTHORITY` - The emitter authority of the token bridge +//! - `TOKEN_BRIDGE_CUSTODY_AUTHORITY` - The custody authority of the token bridge +//! - `TOKEN_BRIDGE_MINT_AUTHORITY` - The mint authority of the token bridge +//! - `TOKEN_BRIDGE_TRANSFER_AUTHORITY` - The transfer authority of the token bridge +//! - `USDC_MINT` - The mint address of USDC +//! - `GUARDIAN_SECRET_KEY` - The guardian secret key +//! - `TOKEN_ROUTER_PID` - The program ID of the token router +//! - `CCTP_TOKEN_MESSENGER_MINTER_PID` - The program ID of the CCTP token messenger minter +//! - `CCTP_MESSAGE_TRANSMITTER_PID` - The program ID of the CCTP message transmitter +//! - `WORMHOLE_POST_MESSAGE_SHIM_PID` - The program ID of the Wormhole post message shim +//! - `WORMHOLE_VERIFY_VAA_SHIM_PID` - The program ID of the Wormhole verify VAA shim +//! - `WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY` - The event authority of the Wormhole post message shim +//! +//! ## Enums +//! +//! - `Chain` - An enum representing the different chains. Chain implements `as_cctp_domain` to get the CCTP domain for the chain. +//! +//! ## Examples +//! +//! ```rust +//! use crate::constants::*; +//! let eth_cctp_domain = Chain::Ethereum.as_cctp_domain(); +//! ``` + use solana_program::pubkey; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; @@ -79,36 +113,28 @@ pub const WORMHOLE_POST_MESSAGE_SHIM_EVENT_AUTHORITY_BUMP: u8 = 255; // pub const PLAYER_ONE_KEYPAIR_B64: &str = "4STrqllKVVva0Fphqyf++6uGTVReATBe2cI26oIuVBft77CQP9qQrMTU1nM9ql0EnCpSgmCmm20m8khMo9WdPQ=="; /// Keypairs as base58 strings (taken from consts.ts in ts tests using a converter) -#[allow(dead_code)] pub const PAYER_KEYPAIR_B58: &str = "4NMwxzmYj2uvHuq8xoqhY8RXg0Pd5zkvmfWAL6YvbYFuViXVCBDK5Pru9GgqEVEZo6UXcPVH6rdR8JKgKxHGkXDp"; -#[allow(dead_code)] pub const OWNER_ASSISTANT_KEYPAIR_B58: &str = "2UbUgoidcNHxVEDG6ADNKGaGDqBTXTVw6B9pWvJtLNhbxcQDkdeEyBYBYYYxxDy92ckXUEaU9chWEGi5jc8Uc9e3"; -#[allow(dead_code)] pub const OWNER_KEYPAIR_B58: &str = "3M5rkG5DQVEGQFRtA1qruxPqJvYBbkGCdkCdB9ZjcnQnYL9ec8W78pLcQHVtjJzHP8phUXQ8V1SXbgZK9ZaFaS6U"; -#[allow(dead_code)] pub const PLAYER_ONE_KEYPAIR_B58: &str = "yqJrKqGqzuW6nEmfj62AgvZWqgGv9TqxfvPXiGvf8DxGDWz3UNkQdDfKDnBYpHQxPRVrYMupDKqbGVYHhfZApGb"; // Helper functions to get keypairs -#[allow(dead_code)] pub fn get_payer_keypair() -> Keypair { Keypair::from_base58_string(PAYER_KEYPAIR_B58) } -#[allow(dead_code)] pub fn get_owner_assistant_keypair() -> Keypair { Keypair::from_base58_string(OWNER_ASSISTANT_KEYPAIR_B58) } -#[allow(dead_code)] pub fn get_owner_keypair() -> Keypair { Keypair::from_base58_string(OWNER_KEYPAIR_B58) } -#[allow(dead_code)] pub fn get_player_one_keypair() -> Keypair { Keypair::from_base58_string(PLAYER_ONE_KEYPAIR_B58) } diff --git a/solana/modules/matching-engine-testing/tests/utils/mint.rs b/solana/modules/matching-engine-testing/tests/utils/mint.rs index 03386bfb4..c97420030 100644 --- a/solana/modules/matching-engine-testing/tests/utils/mint.rs +++ b/solana/modules/matching-engine-testing/tests/utils/mint.rs @@ -1,3 +1,7 @@ +//! # Mint fixture +//! +//! This module provides a fixture for creating a mint account (like a USDC mint). + use anchor_spl::token::spl_token; use solana_cli_output::CliAccount; use solana_program_test::ProgramTestContext; diff --git a/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs index 550de2ad2..927f5d1af 100644 --- a/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs @@ -1,3 +1,9 @@ +//! # Program Fixtures +//! +//! This module provides fixtures for initializing programs on the Solana blockchain. +//! It includes functions to initialize the upgrade manager, CCTP token messenger minter, +//! wormhole core bridge, CCTP message transmitter, local token router, and verify shims. + use solana_program::bpf_loader_upgradeable; use solana_program_test::ProgramTest; use solana_sdk::pubkey::Pubkey; diff --git a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs index 6decae147..32e7478ae 100644 --- a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs +++ b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs @@ -1,3 +1,8 @@ +//! # Public Keys +//! +//! This module provides a struct for representing public keys in the test environment. +//! It includes methods for converting between different key types and for creating unique keys. + use solana_sdk::{keccak, pubkey::Pubkey}; use super::Chain; diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index db10beebf..d7fc8a15f 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -1,4 +1,7 @@ -// Add methods for adding endpoints to the program test +//! # Router +//! +//! This module provides a struct for representing a router in the test environment. +//! It includes methods for adding router endpoints to the program test environment. use super::constants::*; use super::token_account::create_token_account_for_pda; diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index 4c05b4d1d..ea7c6e878 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -1,3 +1,8 @@ +//! # Token Account +//! +//! This module provides a fixture for creating a token account. +//! It includes methods for creating a token account and for reading a keypair from a JSON fixture file. + use anchor_spl::associated_token::spl_associated_token_account; use anchor_spl::token::spl_token; use solana_program_test::ProgramTestContext; diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index b9a37d751..489c6e8dd 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -1,3 +1,8 @@ +//! # VAA +//! +//! This module provides a struct for representing a VAA in the test environment. +//! It includes methods for creating a VAA and for deserializing a VAA. + use anchor_lang::prelude::*; use common::messages::wormhole_io::{TypePrefixedPayload, WriteableBytes}; use common::messages::{FastMarketOrder, SlowOrderResponse}; From bb4543fb94da289c1773d071bbdc0729cc6a6530 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 2 Apr 2025 15:07:17 +0100 Subject: [PATCH 053/112] added error code, and some program checks --- solana/programs/matching-engine/src/error.rs | 8 +++++++- .../fallback/processor/close_fast_market_order.rs | 15 +++++++++------ .../src/fallback/processor/execute_order.rs | 1 - .../processor/initialise_fast_market_order.rs | 3 --- .../src/fallback/processor/place_initial_offer.rs | 8 +++++++- .../fallback/processor/prepare_order_response.rs | 5 +---- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index 4b54bc9fe..e6a424264 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -100,9 +100,15 @@ pub enum MatchingEngineError { TokenTransferFailed = 0x70c, InvalidMint = 0x70e, + // Place initial offer errors #[msg("From and to router endpoints are the same")] SameEndpoints = 0x800, - InvalidCctpMessage = 0x802, + + // Close fast market order errors + MismatchingCloseAccountRefundRecipient = 0xa10, + + // Execute order errors + InvalidCctpMessage = 0x902, } #[cfg(test)] diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 285bbe9b6..89d8e64e1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -1,3 +1,4 @@ +use crate::error::MatchingEngineError; use crate::state::FastMarketOrder; use anchor_lang::prelude::*; use solana_program::instruction::Instruction; @@ -63,12 +64,14 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { if fast_market_order_data.close_account_refund_recipient != close_account_refund_recipient.key().as_ref() { - return Err(ProgramError::InvalidAccountData.into()).map_err(|e: Error| { - e.with_pubkeys(( - Pubkey::from(fast_market_order_data.close_account_refund_recipient), - close_account_refund_recipient.key(), - )) - }); + return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( + |e: Error| { + e.with_pubkeys(( + Pubkey::from(fast_market_order_data.close_account_refund_recipient), + close_account_refund_recipient.key(), + )) + }, + ); } // First, get the current lamports value diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 57eb9bb42..c9d724173 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -358,7 +358,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }); }; - // TODO: Done with auction checks, now on to executor token checks if executor_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { msg!("Executor token is not equal to best offer token"); return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 302fb98ab..83e2e91ec 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -122,7 +122,6 @@ pub fn initialise_fast_market_order( guardian_set_bump, _padding: _, } = *data; - // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ let fast_market_order_digest = fast_market_order.digest(); @@ -191,7 +190,6 @@ pub fn initialise_fast_market_order( &program_id, fast_market_order_signer_seeds, )?; - // Borrow the account data mutably let mut fast_market_order_account_data = fast_market_order_account.try_borrow_mut_data()?; @@ -206,7 +204,6 @@ pub fn initialise_fast_market_order( msg!("Account data buffer too small"); return Err(MatchingEngineError::AccountDataTooSmall.into()); } - // Write the fast_market_order struct to the account fast_market_order_account_data[8..8_usize.saturating_add(fast_market_order_bytes.len())] .copy_from_slice(fast_market_order_bytes); diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 4b1bd989e..3b3194493 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -182,7 +182,6 @@ pub fn place_initial_offer_cctp_shim( // Check all accounts are valid check_account_length(accounts, 11)?; // Extract data fields - // TODO: Remove sequence, vaa_time because they are in the fast market order state let PlaceInitialOfferCctpShimData { offer_price } = *data; let signer = &accounts[0]; @@ -198,6 +197,13 @@ pub fn place_initial_offer_cctp_shim( let auction_custody_token = &accounts[9]; let usdc = &accounts[10]; + // Check that the fast market order account is owned by the program + if fast_market_order_account.owner != program_id { + msg!("Fast market order account owner is invalid"); + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name("fast_market_order_account")); + } + let fast_market_order_zero_copy = FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 8a79d1ba9..b236f6b96 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -172,7 +172,6 @@ impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { } } -// TODO: Also close the fast market order account since it is no longer needed pub struct PrepareOrderResponseCctpShim<'ix> { pub program_id: &'ix Pubkey, pub accounts: PrepareOrderResponseCctpShimAccounts<'ix>, @@ -256,7 +255,6 @@ pub fn prepare_order_response_cctp_shim( FastMarketOrderState::try_read(&fast_market_order_account_data[..])?; // Create pdas for addresses that need to be created // Check the prepared order response account is valid - // TODO: Pass the digest so it isn't recomputed let fast_market_order_digest = fast_market_order_zero_copy.digest(); // Construct the finalised vaa message digest data let finalized_vaa_message_digest = { @@ -286,7 +284,6 @@ pub fn prepare_order_response_cctp_shim( Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; // Deserialise the to_endpoint account - // TODO: Scope this to do checks and deallocate stack let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..]).map(Box::new)?; // Deserialise the from_endpoint account @@ -296,7 +293,7 @@ pub fn prepare_order_response_cctp_shim( let guardian_set_bump = finalized_vaa_message.guardian_set_bump; // Check loaded vaa is deposit message - // TODO: Fix errors + // TODO: Fix errors to be more specific let liquidity_layer_message = LiquidityLayerDepositMessage::parse(&finalized_vaa_message.deposit_payload) .map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; From f5d739cf1d986faf8f54d1a01f7edba191f2e68a Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 2 Apr 2025 15:12:29 +0100 Subject: [PATCH 054/112] fixed filestrings in testing_scenarios dir --- .../matching-engine-testing/tests/test_scenarios/make_offer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index cd214de97..50a380aa8 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -1,7 +1,7 @@ #![allow(clippy::expect_used)] #![allow(clippy::panic)] -//! # Make offer instruction testing +//! # Place initial offer and improve offer instruction testing //! //! This module contains tests for the place initial offer and improve offer instructions. //! From b8a67b7b76adcf5623cae3103a0401cba51c73e1 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 7 Apr 2025 13:57:54 +0100 Subject: [PATCH 055/112] tests and lint pass --- .../tests/fixtures/usdt_mint.json | 14 + .../tests/shimful/shims_execute_order.rs | 223 +++--- .../tests/shimful/shims_make_offer.rs | 124 +++- .../tests/shimless/initialize.rs | 7 +- .../tests/shimless/make_offer.rs | 184 +++-- .../create_and_close_fast_market_order.rs | 10 +- .../tests/test_scenarios/execute_order.rs | 32 +- .../test_scenarios/initialise_and_misc.rs | 15 +- .../tests/test_scenarios/make_offer.rs | 639 +++++++++++++++--- .../tests/test_scenarios/prepare_order.rs | 17 +- .../tests/test_scenarios/settle_auction.rs | 2 +- .../tests/testing_engine/config.rs | 79 ++- .../tests/testing_engine/engine.rs | 141 +--- .../tests/testing_engine/setup.rs | 138 +++- .../tests/testing_engine/state.rs | 14 + .../tests/utils/airdrop.rs | 8 +- .../tests/utils/auction.rs | 21 +- .../tests/utils/constants.rs | 3 + .../tests/utils/router.rs | 24 +- .../tests/utils/token_account.rs | 7 + .../tests/utils/vaa.rs | 10 +- .../processor/close_fast_market_order.rs | 7 + .../processor/initialise_fast_market_order.rs | 12 +- .../fallback/processor/place_initial_offer.rs | 2 +- .../src/processor/auction/offer/improve.rs | 4 +- .../auction/offer/place_initial/cctp.rs | 2 +- .../src/state/fast_market_order.rs | 2 +- .../matching-engine/src/utils/auction.rs | 8 +- 28 files changed, 1309 insertions(+), 440 deletions(-) create mode 100644 solana/modules/matching-engine-testing/tests/fixtures/usdt_mint.json diff --git a/solana/modules/matching-engine-testing/tests/fixtures/usdt_mint.json b/solana/modules/matching-engine-testing/tests/fixtures/usdt_mint.json new file mode 100644 index 000000000..6ffcd4c0d --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/fixtures/usdt_mint.json @@ -0,0 +1,14 @@ +{ + "pubkey": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", + "account": { + "lamports": 14801671630, + "data": [ + "AQAAAAXqnPFs5BGY8aSZN8iMNwqU1K//ibW6y470XmMku3j3wYJlUqF9CAAGAQEAAAAF6pzxbOQRmPGkmTfIjDcKlNSv/4m1usuO9F5jJLt49w==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 7b6bb9385..be1f81da4 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -1,6 +1,9 @@ -use crate::testing_engine::config::ExpectedError; -use crate::testing_engine::setup::{Solver, TestingContext, TransferDirection}; -use crate::utils::auction::ActiveAuctionState; +use crate::testing_engine::config::{ + ExecuteOrderInstructionConfig, ExpectedError, InstructionConfig, +}; +use crate::testing_engine::setup::{TestingContext, TransferDirection}; +use crate::testing_engine::state::TestingEngineState; +use crate::utils::token_account::SplTokenEnum; use super::super::utils; use anchor_spl::token::spl_token; @@ -9,10 +12,7 @@ use common::wormhole_cctp_solana::cctp::{ }; use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; use solana_program_test::ProgramTestContext; -use solana_sdk::{ - pubkey::Pubkey, signature::Keypair, signer::Signer, sysvar::SysvarId, transaction::Transaction, -}; -use std::rc::Rc; +use solana_sdk::{pubkey::Pubkey, signer::Signer, sysvar::SysvarId, transaction::Transaction}; use utils::constants::*; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; use wormhole_svm_definitions::{ @@ -40,13 +40,17 @@ pub struct ExecuteOrderFallbackAccounts { impl ExecuteOrderFallbackAccounts { pub fn new( - auction_accounts: &utils::auction::AuctionAccounts, - fast_market_order_address: &Pubkey, - active_auction_state: &ActiveAuctionState, - signer: &Pubkey, + current_state: &TestingEngineState, + payer_signer: &Pubkey, fixture_accounts: &utils::account_fixtures::FixtureAccounts, - transfer_direction: TransferDirection, ) -> Self { + let transfer_direction = current_state.base().transfer_direction; + let auction_accounts = current_state.auction_accounts().unwrap(); + let active_auction_state = current_state.auction_state().get_active_auction().unwrap(); + let fast_market_order_address = current_state + .fast_market_order() + .unwrap() + .fast_market_order_address; let remote_token_messenger = match transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.arbitrum_remote_token_messenger @@ -58,15 +62,15 @@ impl ExecuteOrderFallbackAccounts { }; Self { - signer: *signer, + signer: *payer_signer, custodian: auction_accounts.custodian, - fast_market_order_address: *fast_market_order_address, + fast_market_order_address, active_auction: active_auction_state.auction_address, active_auction_custody_token: active_auction_state.auction_custody_token_address, active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, - initial_participant: *signer, + initial_participant: *payer_signer, to_router_endpoint: auction_accounts.to_router_endpoint, remote_token_messenger, token_messenger: fixture_accounts.token_messenger, @@ -87,18 +91,70 @@ pub struct ExecuteOrderFallbackFixtureAccounts { pub remote_token_messenger: Pubkey, pub token_messenger_minter_sender_authority: Pubkey, pub token_messenger_minter_event_authority: Pubkey, + pub messenger_transmitter_config: Pubkey, + pub token_minter: Pubkey, + pub executor_token: Pubkey, } pub async fn execute_order_fallback( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - payer_signer: &Rc, - program_id: &Pubkey, - solver: Solver, + config: &ExecuteOrderInstructionConfig, execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, expected_error: Option<&ExpectedError>, ) -> Option { - // Get target chain and use as remote address + let program_id = &testing_context.get_matching_engine_program_id(); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + + let execute_order_fallback_fixture = create_execute_order_fallback_fixture( + testing_context, + config, + execute_order_fallback_accounts, + ); + let clock_id = solana_program::clock::Clock::id(); + let execute_order_ix_accounts = create_execute_order_shim_accounts( + execute_order_fallback_accounts, + &execute_order_fallback_fixture, + &clock_id, + ); + + let execute_order_ix = ExecuteOrderCctpShim { + program_id, + accounts: execute_order_ix_accounts, + } + .instruction(); + + // Considering fast forwarding blocks here for deadline to be reached + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(); + crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; + let transaction = Transaction::new_signed_with_payer( + &[execute_order_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + recent_blockhash, + ); + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + if expected_error.is_none() { + Some(execute_order_fallback_fixture) + } else { + None + } +} + +pub fn create_execute_order_fallback_fixture( + testing_context: &TestingContext, + config: &ExecuteOrderInstructionConfig, + execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, +) -> ExecuteOrderFallbackFixture { + let program_id = &testing_context.get_matching_engine_program_id(); let cctp_message = Pubkey::find_program_address( &[ common::CCTP_MESSAGE_SEED_PREFIX, @@ -134,11 +190,36 @@ pub async fn execute_order_fallback( &POST_MESSAGE_SHIM_PROGRAM_ID, ) .0; - let executor_token = solver.actor.token_account_address().unwrap(); + let solver = testing_context.testing_actors.solvers[config.solver_index].clone(); + let executor_token = solver + .actor + .token_account_address(&SplTokenEnum::Usdc) + .unwrap(); + ExecuteOrderFallbackFixture { + cctp_message, + post_message_sequence, + post_message_message, + accounts: ExecuteOrderFallbackFixtureAccounts { + local_token, + token_messenger, + remote_token_messenger, + token_messenger_minter_sender_authority, + token_messenger_minter_event_authority: *token_messenger_minter_event_authority, + messenger_transmitter_config, + token_minter, + executor_token, + }, + } +} - let execute_order_ix_accounts = ExecuteOrderShimAccounts { - signer: &payer_signer.pubkey(), // 0 - cctp_message: &cctp_message, // 1 +pub fn create_execute_order_shim_accounts<'ix>( + execute_order_fallback_accounts: &'ix ExecuteOrderFallbackAccounts, + execute_order_fallback_fixture: &'ix ExecuteOrderFallbackFixture, + clock_id: &'ix Pubkey, +) -> ExecuteOrderShimAccounts<'ix> { + ExecuteOrderShimAccounts { + signer: &execute_order_fallback_accounts.signer, // 0 + cctp_message: &execute_order_fallback_fixture.cctp_message, // 1 custodian: &execute_order_fallback_accounts.custodian, // 2 fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, // 3 active_auction: &execute_order_fallback_accounts.active_auction, // 4 @@ -146,23 +227,33 @@ pub async fn execute_order_fallback( active_auction_config: &execute_order_fallback_accounts.active_auction_config, // 6 active_auction_best_offer_token: &execute_order_fallback_accounts .active_auction_best_offer_token, // 7 - executor_token: &executor_token, // 8 + executor_token: &execute_order_fallback_fixture.accounts.executor_token, // 8 initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, // 9 initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 - post_message_sequence: &post_message_sequence, // 13 - post_message_message: &post_message_message, // 14 + post_message_sequence: &execute_order_fallback_fixture.post_message_sequence, // 13 + post_message_message: &execute_order_fallback_fixture.post_message_message, // 14 cctp_deposit_for_burn_mint: &USDC_MINT, // 15 cctp_deposit_for_burn_token_messenger_minter_sender_authority: - &token_messenger_minter_sender_authority, // 16 - cctp_deposit_for_burn_message_transmitter_config: &messenger_transmitter_config, // 17 - cctp_deposit_for_burn_token_messenger: &token_messenger, // 18 - cctp_deposit_for_burn_remote_token_messenger: &remote_token_messenger, // 19 - cctp_deposit_for_burn_token_minter: &token_minter, // 20 - cctp_deposit_for_burn_local_token: &local_token, // 21 + &execute_order_fallback_fixture + .accounts + .token_messenger_minter_sender_authority, // 16 + cctp_deposit_for_burn_message_transmitter_config: &execute_order_fallback_fixture + .accounts + .messenger_transmitter_config, // 17 + cctp_deposit_for_burn_token_messenger: &execute_order_fallback_fixture + .accounts + .token_messenger, // 18 + cctp_deposit_for_burn_remote_token_messenger: &execute_order_fallback_fixture + .accounts + .remote_token_messenger, // 19 + cctp_deposit_for_burn_token_minter: &execute_order_fallback_fixture.accounts.token_minter, // 20 + cctp_deposit_for_burn_local_token: &execute_order_fallback_fixture.accounts.local_token, // 21 cctp_deposit_for_burn_token_messenger_minter_event_authority: - token_messenger_minter_event_authority, // 22 + &execute_order_fallback_fixture + .accounts + .token_messenger_minter_event_authority, // 22 cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 @@ -171,74 +262,30 @@ pub async fn execute_order_fallback( post_message_shim_event_authority: &POST_MESSAGE_SHIM_EVENT_AUTHORITY, // 28 system_program: &solana_program::system_program::ID, // 29 token_program: &spl_token::ID, // 30 - clock: &solana_program::clock::Clock::id(), // 31 - }; - - let execute_order_ix = ExecuteOrderCctpShim { - program_id, - accounts: execute_order_ix_accounts, - } - .instruction(); - - // Considering fast forwarding blocks here for deadline to be reached - let recent_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .unwrap(); - crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; - let transaction = Transaction::new_signed_with_payer( - &[execute_order_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - recent_blockhash, - ); - testing_context - .execute_and_verify_transaction(test_context, transaction, expected_error) - .await; - if expected_error.is_none() { - Some(ExecuteOrderFallbackFixture { - cctp_message, - post_message_sequence, - post_message_message, - accounts: ExecuteOrderFallbackFixtureAccounts { - local_token, - token_messenger, - remote_token_messenger, - token_messenger_minter_sender_authority, - token_messenger_minter_event_authority: *token_messenger_minter_event_authority, - }, - }) - } else { - None + clock: clock_id, // 31 } } pub async fn execute_order_fallback_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - auction_accounts: &utils::auction::AuctionAccounts, - fast_market_order_address: &Pubkey, - active_auction_state: &ActiveAuctionState, - solver: Solver, - expected_error: Option<&ExpectedError>, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, ) -> Option { + let expected_error = config.expected_error(); let fixture_accounts = testing_context .get_fixture_accounts() .expect("Pre-made fixture accounts not found"); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( - auction_accounts, - fast_market_order_address, - active_auction_state, - &testing_context.testing_actors.owner.pubkey(), - &fixture_accounts, - testing_context.transfer_direction, - ); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + let execute_order_fallback_accounts = + ExecuteOrderFallbackAccounts::new(current_state, &payer_signer.pubkey(), &fixture_accounts); execute_order_fallback( testing_context, test_context, - &testing_context.testing_actors.owner.keypair(), - &testing_context.get_matching_engine_program_id(), - solver, + config, &execute_order_fallback_accounts, expected_error, ) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 5d3256386..520548124 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -1,8 +1,9 @@ -use crate::testing_engine::config::ExpectedError; -use crate::testing_engine::state::InitialOfferPlacedState; +use crate::testing_engine::config::{ExpectedError, PlaceInitialOfferInstructionConfig}; +use crate::testing_engine::state::{InitialOfferPlacedState, TestingEngineState}; +use crate::utils::auction::AuctionAccounts; use super::super::utils; -use crate::testing_engine::setup::{TestingActor, TestingContext}; +use crate::testing_engine::setup::TestingContext; use matching_engine::fallback::place_initial_offer::{ PlaceInitialOfferCctpShim as PlaceInitialOfferCctpShimFallback, PlaceInitialOfferCctpShimAccounts as PlaceInitialOfferCctpShimFallbackAccounts, @@ -12,8 +13,7 @@ use matching_engine::state::Auction; use solana_program_test::ProgramTestContext; use super::fast_market_order_shim::create_fast_market_order_state_from_vaa_data; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; -use std::rc::Rc; +use solana_sdk::{pubkey::Pubkey, signer::Signer, transaction::Transaction}; /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. /// @@ -36,21 +36,55 @@ use std::rc::Rc; /// /// * The expected error is reached /// * If successful, the solver's USDC balance should decrease by the offer price -#[allow(clippy::too_many_arguments)] pub async fn place_initial_offer_fallback( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - payer_signer: &Rc, - vaa_data: &utils::vaa::PostedVaaData, - actor: TestingActor, - fast_market_order_account: &Pubkey, - auction_accounts: &utils::auction::AuctionAccounts, - offer_price: u64, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, expected_error: Option<&ExpectedError>, ) -> Option { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + let close_account_refund_recipient = current_state + .fast_market_order() + .unwrap() + .close_account_refund_recipient; + let fast_market_order_address = match &config.fast_market_order_address { + Some(fast_market_order_address) => *fast_market_order_address, + None => { + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address + } + }; + let auction_config_address = current_state.auction_config_address().unwrap(); + let custodian_address = current_state.custodian_address().unwrap(); let program_id = testing_context.get_matching_engine_program_id(); - let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, actor.pubkey()); - + let fast_transfer_vaa = ¤t_state + .base() + .vaas + .get(config.test_vaa_pair_index) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let vaa_data = fast_transfer_vaa.get_vaa_data(); + let fast_market_order = + create_fast_market_order_state_from_vaa_data(vaa_data, close_account_refund_recipient); + let offer_price = config.offer_price; + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); + let offer_token = match &config.custom_accounts { + Some(custom_accounts) => match custom_accounts.offer_token_address { + Some(offer_token_address) => offer_token_address, + None => offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(), + }, + None => offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(), + }; let auction_address = Pubkey::find_program_address( &[Auction::SEED_PREFIX, &fast_market_order.digest()], &program_id, @@ -76,26 +110,44 @@ pub async fn place_initial_offer_fallback( ) .0; - actor - .approve_usdc(test_context, &transfer_authority, 420_000__000_000) + offer_actor + .approve_spl_token( + test_context, + &transfer_authority, + 420_000__000_000, + &config.spl_token_enum, + ) .await; - let actor_usdc_balance_before = actor.get_token_account_balance(test_context).await; + let actor_usdc_balance_before = offer_actor + .get_token_account_balance(test_context, &config.spl_token_enum) + .await; let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price); + let (from_router_endpoint, to_router_endpoint) = + config.get_from_and_to_router_endpoints(current_state); + + let usdc_mint_address = match &config.custom_accounts { + Some(custom_accounts) => match custom_accounts.mint_address { + Some(usdc_mint_address) => usdc_mint_address, + None => testing_context.get_usdc_mint_address(), + }, + None => testing_context.get_usdc_mint_address(), + }; + let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { signer: &payer_signer.pubkey(), transfer_authority: &transfer_authority, - custodian: &auction_accounts.custodian, - auction_config: &auction_accounts.auction_config, - from_endpoint: &auction_accounts.from_router_endpoint, - to_endpoint: &auction_accounts.to_router_endpoint, - fast_market_order: fast_market_order_account, + custodian: &custodian_address, + auction_config: &auction_config_address, + from_endpoint: &from_router_endpoint, + to_endpoint: &to_router_endpoint, + fast_market_order: &fast_market_order_address, auction: &auction_address, - offer_token: &auction_accounts.offer_token, + offer_token: &offer_token, auction_custody_token: &auction_custody_token_address, - usdc: &auction_accounts.usdc_mint, + usdc: &usdc_mint_address, system_program: &solana_program::system_program::ID, token_program: &anchor_spl::token::spl_token::ID, }; @@ -122,7 +174,9 @@ pub async fn place_initial_offer_fallback( .execute_and_verify_transaction(test_context, transaction, expected_error) .await; if expected_error.is_none() { - let actor_usdc_balance_after = actor.get_token_account_balance(test_context).await; + let actor_usdc_balance_after = offer_actor + .get_token_account_balance(test_context, &config.spl_token_enum) + .await; assert!( actor_usdc_balance_after < actor_usdc_balance_before, "Solver USDC balance should have decreased" @@ -130,22 +184,36 @@ pub async fn place_initial_offer_fallback( let new_active_auction_state = utils::auction::ActiveAuctionState { auction_address, auction_custody_token_address, - auction_config_address: auction_accounts.auction_config, + auction_config_address, initial_offer: utils::auction::AuctionOffer { participant: payer_signer.pubkey(), - offer_token: auction_accounts.offer_token, + offer_token, offer_price, }, best_offer: utils::auction::AuctionOffer { participant: payer_signer.pubkey(), - offer_token: auction_accounts.offer_token, + offer_token, offer_price, }, + spl_token_enum: config.spl_token_enum.clone(), }; let new_auction_state = utils::auction::AuctionState::Active(Box::new(new_active_auction_state)); Some(InitialOfferPlacedState { auction_state: new_auction_state, + auction_accounts: AuctionAccounts::new( + Some(fast_transfer_vaa.get_vaa_pubkey()), + offer_actor.clone(), + current_state.close_account_refund_recipient(), + auction_config_address, + ¤t_state + .router_endpoints() + .expect("Router endpoints are not created") + .endpoints, + custodian_address, + config.spl_token_enum.clone(), + current_state.base().transfer_direction, + ), }) } else { None diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 97eb98add..93bc25fab 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -10,7 +10,10 @@ use anchor_lang::AccountDeserialize; use anchor_spl::{associated_token::spl_associated_token_account, token::spl_token}; use solana_program::{bpf_loader_upgradeable, system_program}; -use crate::testing_engine::config::{ExpectedError, ExpectedLog}; +use crate::{ + testing_engine::config::{ExpectedError, ExpectedLog}, + utils::token_account::SplTokenEnum, +}; use crate::testing_engine::setup::TestingContext; use anchor_lang::{InstructionData, ToAccountMetas}; @@ -176,7 +179,7 @@ pub async fn initialize_program( fee_recipient_token: testing_context .testing_actors .fee_recipient - .token_account_address() + .token_account_address(&SplTokenEnum::Usdc) .unwrap(), cctp_mint_recipient, usdc: matching_engine::accounts::Usdc { diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index c42faaa93..ced3459fa 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -1,12 +1,19 @@ use std::rc::Rc; use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::ImproveOfferInstructionConfig; +use crate::testing_engine::config::InstructionConfig; +use crate::testing_engine::config::PlaceInitialOfferInstructionConfig; +use crate::testing_engine::setup::TestingActor; +use crate::testing_engine::state::InitialOfferPlacedState; +use crate::testing_engine::state::TestingEngineState; +use crate::utils::auction::AuctionAccounts; use super::super::utils; use anchor_lang::prelude::*; use anchor_lang::InstructionData; -use crate::testing_engine::setup::{Solver, TestingContext}; +use crate::testing_engine::setup::TestingContext; use common::TRANSFER_AUTHORITY_SEED_PREFIX; use matching_engine::accounts::ImproveOffer as ImproveOfferAccounts; use matching_engine::accounts::{ @@ -22,8 +29,7 @@ use solana_sdk::instruction::Instruction; use solana_sdk::signature::Keypair; use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; -use utils::auction::{ActiveAuctionState, AuctionAccounts, AuctionOffer, AuctionState}; -use utils::vaa::TestVaa; +use utils::auction::{ActiveAuctionState, AuctionOffer, AuctionState}; /// Place an initial offer (shimless) /// @@ -45,15 +51,35 @@ use utils::vaa::TestVaa; pub async fn place_initial_offer_shimless( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - accounts: &AuctionAccounts, - fast_market_order: &TestVaa, - offer_price: u64, - payer_signer: &Rc, - expected_error: Option<&ExpectedError>, -) -> AuctionState { + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, +) -> Option { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); + let offer_token = offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(); + let expected_error = config.expected_error(); + let fast_vaa = ¤t_state + .base() + .vaas + .get(config.test_vaa_pair_index) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let auction_config_address = current_state + .initialized() + .expect("Testing state is not initialized") + .auction_config_address; + let custodian_address = current_state + .initialized() + .expect("Testing state is not initialized") + .custodian_address; let program_id = testing_context.get_matching_engine_program_id(); let auction_address = Pubkey::find_program_address( - &[Auction::SEED_PREFIX, &fast_market_order.vaa_data.digest()], + &[Auction::SEED_PREFIX, &fast_vaa.vaa_data.digest()], &program_id, ) .0; @@ -65,18 +91,51 @@ pub async fn place_initial_offer_shimless( &program_id, ) .0; - let initial_offer_ix = PlaceInitialOfferCctpIx { offer_price }; - + let initial_offer_ix = PlaceInitialOfferCctpIx { + offer_price: config.offer_price, + }; + let (from_router_endpoint, to_router_endpoint) = match &config.custom_accounts { + Some(custom_accounts) => { + let from_router_endpoint = match custom_accounts.from_router_endpoint { + Some(from_router_endpoint) => from_router_endpoint, + None => { + current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses(current_state.base().transfer_direction) + .0 + } + }; + let to_router_endpoint = match custom_accounts.to_router_endpoint { + Some(to_router_endpoint) => to_router_endpoint, + None => { + current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses(current_state.base().transfer_direction) + .1 + } + }; + (from_router_endpoint, to_router_endpoint) + } + None => current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses(current_state.base().transfer_direction), + }; let fast_order_path = FastOrderPath { fast_vaa: LiquidityLayerVaa { - vaa: fast_market_order.vaa_pubkey, + vaa: fast_vaa.vaa_pubkey, }, path: LiveRouterPath { from_endpoint: LiveRouterEndpoint { - endpoint: accounts.from_router_endpoint, + endpoint: from_router_endpoint, }, to_endpoint: LiveRouterEndpoint { - endpoint: accounts.to_router_endpoint, + endpoint: to_router_endpoint, }, }, }; @@ -93,7 +152,9 @@ pub async fn place_initial_offer_shimless( .0; { // Check if solver has already approved usdc - let usdc_account = accounts.actor.token_account_address().unwrap(); + let usdc_account = offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(); let usdc_account_info = test_context .banks_client .get_account(usdc_account) @@ -105,35 +166,52 @@ pub async fn place_initial_offer_shimless( ) .expect("Failed to deserialize usdc account"); if token_account_info.delegate.is_none() { - accounts - .actor - .approve_usdc(test_context, &transfer_authority, 420_000__000_000) + offer_actor + .approve_spl_token( + test_context, + &transfer_authority, + 420_000__000_000, + &config.spl_token_enum, + ) .await; } else { let delegate = token_account_info.delegate.unwrap(); if delegate != transfer_authority { - accounts - .actor - .approve_usdc(test_context, &transfer_authority, 420_000__000_000) + offer_actor + .approve_spl_token( + test_context, + &transfer_authority, + 420_000__000_000, + &config.spl_token_enum, + ) .await; } } } let custodian = CheckedCustodian { - custodian: accounts.custodian, + custodian: custodian_address, + }; + let usdc_mint_address = match &config.custom_accounts { + Some(custom_accounts) => match custom_accounts.mint_address { + Some(usdc_mint_address) => usdc_mint_address, + None => testing_context.get_usdc_mint_address(), + }, + None => testing_context.get_usdc_mint_address(), }; let initial_offer_accounts = PlaceInitialOfferCctpAccounts { payer: payer_signer.pubkey(), transfer_authority, custodian, - auction_config: accounts.auction_config, + auction_config: auction_config_address, fast_order_path, auction: auction_address, - offer_token: accounts.offer_token, + offer_token: offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(), auction_custody_token: auction_custody_token_address, usdc: Usdc { - mint: accounts.usdc_mint, + mint: usdc_mint_address, }, system_program: anchor_lang::system_program::ID, token_program: anchor_spl::token::ID, @@ -143,7 +221,7 @@ pub async fn place_initial_offer_shimless( let mut account_metas = initial_offer_accounts.to_account_metas(None); for meta in account_metas.iter_mut() { - if meta.pubkey == accounts.offer_token { + if meta.pubkey == offer_token { meta.is_writable = true; } } @@ -157,7 +235,7 @@ pub async fn place_initial_offer_shimless( let tx = Transaction::new_signed_with_payer( &[initial_offer_ix_anchor], Some(&payer_signer.pubkey()), - &[payer_signer], + &[&payer_signer], testing_context .get_new_latest_blockhash(test_context) .await @@ -170,23 +248,42 @@ pub async fn place_initial_offer_shimless( // If the transaction failed and we expected it to pass, we would not get here if expected_error.is_none() { - AuctionState::Active(Box::new(ActiveAuctionState { + let auction_state = AuctionState::Active(Box::new(ActiveAuctionState { auction_address, auction_custody_token_address, - auction_config_address: accounts.auction_config, + auction_config_address, initial_offer: AuctionOffer { participant: payer_signer.pubkey(), - offer_token: accounts.offer_token, + offer_token, offer_price: initial_offer_ix.offer_price, }, best_offer: AuctionOffer { participant: payer_signer.pubkey(), - offer_token: accounts.offer_token, + offer_token, offer_price: initial_offer_ix.offer_price, }, - })) + spl_token_enum: config.spl_token_enum.clone(), + })); + + let auction_accounts = AuctionAccounts::new( + Some(fast_vaa.get_vaa_pubkey()), + offer_actor.clone(), + current_state.close_account_refund_recipient(), + auction_config_address, + ¤t_state + .router_endpoints() + .expect("Router endpoints are not created") + .endpoints, + custodian_address, + config.spl_token_enum.clone(), + current_state.base().transfer_direction, + ); + Some(InitialOfferPlacedState { + auction_state, + auction_accounts, + }) } else { - AuctionState::Inactive + None } } @@ -210,8 +307,8 @@ pub async fn place_initial_offer_shimless( pub async fn improve_offer( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - solver: Solver, - offer_price: u64, + actor: &TestingActor, + config: &ImproveOfferInstructionConfig, payer_signer: &Rc, initial_auction_state: &AuctionState, expected_error: Option<&ExpectedError>, @@ -221,7 +318,7 @@ pub async fn improve_offer( let auction_config = active_auction_state.auction_config_address; let auction_address = active_auction_state.auction_address; let auction_custody_token_address = active_auction_state.auction_custody_token_address; - + let offer_price = config.offer_price; let improve_offer_ix = ImproveOfferIx { offer_price }; let event_authority = Pubkey::find_program_address(&[b"__event_authority"], &program_id).0; @@ -234,10 +331,16 @@ pub async fn improve_offer( &program_id, ) .0; - solver - .approve_usdc(test_context, &transfer_authority, 420_000__000_000) + let spl_token_enum = &active_auction_state.spl_token_enum; + actor + .approve_spl_token( + test_context, + &transfer_authority, + 420_000__000_000, + spl_token_enum, + ) .await; - let offer_token = solver.token_account_address().unwrap(); + let offer_token = actor.token_account_address(spl_token_enum).unwrap(); let active_auction = ActiveAuction { auction: auction_address, @@ -298,6 +401,7 @@ pub async fn improve_offer( offer_token, offer_price, }, + spl_token_enum: spl_token_enum.clone(), })) } else { initial_auction_state.clone() diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs index 8e9d44590..fdb307d90 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs @@ -1,4 +1,4 @@ -//! # Happy path instruction testing +//! # Create and close fast market order instruction testing //! //! This module contains tests for the create and close fast market order instructions. //! @@ -76,7 +76,7 @@ pub async fn test_initialise_fast_market_order_fallback() { let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -104,7 +104,7 @@ pub async fn test_close_fast_market_order_fallback() { ), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -138,7 +138,7 @@ pub async fn test_close_fast_market_order_fallback_with_custom_refund_recipient( }), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; let solver_1_balance_after = solver_1.get_lamport_balance(&mut test_context).await; assert!( @@ -217,7 +217,7 @@ pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_in let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 8d61285f9..09431018c 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -49,7 +49,7 @@ pub async fn test_execute_order_fallback() { InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -77,12 +77,36 @@ pub async fn test_execute_order_shimless() { InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } /* - Sad path tests + Sad path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** ** +** ** +** ** + ** ************ ** + ** ****** ****** ** + *** ***** ***** *** + ** *** *** ** + *** ** ** *** + **** **** + **** **** + ****** ****** + ***************** */ /// Test that the execute order fallback instruction blocks the shimless instruction @@ -120,6 +144,6 @@ pub async fn test_execute_order_fallback_blocks_shimless() { }), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs index 87ca79949..f4920fa3d 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs @@ -18,6 +18,7 @@ use crate::shimless; use crate::testing_engine; use crate::testing_engine::config::InitializeInstructionConfig; use crate::utils; +use crate::utils::token_account::SplTokenEnum; use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; use matching_engine::ID as PROGRAM_ID; @@ -78,6 +79,7 @@ pub async fn test_initialize_program() { .execute( &mut test_context, vec![InstructionTrigger::InitializeProgram(initialize_config)], + None, ) .await; } @@ -100,6 +102,7 @@ pub async fn test_cctp_token_router_endpoint_creation() { .execute( &mut test_context, vec![InstructionTrigger::InitializeProgram(initialize_config)], + None, ) .await; } @@ -163,6 +166,7 @@ pub async fn test_setup_vaas() { CreateCctpRouterEndpointsInstructionConfig::default(), ), ], + None, ) .await; } @@ -220,10 +224,17 @@ pub async fn test_approve_usdc() { ) .0; solver - .approve_usdc(&mut test_context, &transfer_authority, offer_price) + .approve_spl_token( + &mut test_context, + &transfer_authority, + offer_price, + &SplTokenEnum::Usdc, + ) .await; - let usdc_balance = solver.get_token_account_balance(&mut test_context).await; + let usdc_balance = solver + .get_token_account_balance(&mut test_context, &SplTokenEnum::Usdc) + .await; // TODO: Create an issue based on this bug. So this function will transfer the ownership of whatever the guardian signatures signer is set to to the verify shim program. This means that the argument to this function MUST be ephemeral and cannot be used until the close signatures instruction has been executed. let _guardian_signature_info = shimful::verify_shim::create_guardian_signatures( diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index 50a380aa8..78847258b 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -22,10 +22,15 @@ use crate::testing_engine::config::{ use crate::testing_engine::state::TestingEngineState; use crate::utils; use crate::utils::auction::compare_auctions; +use crate::utils::token_account::SplTokenEnum; +use crate::utils::vaa::{ + CreateDepositAndFastTransferParams, CreateDepositParams, CreateFastTransferParams, +}; use anchor_lang::error::ErrorCode; use anchor_lang::AccountDeserialize; -use matching_engine::state::Auction; +use matching_engine::error::MatchingEngineError; +use matching_engine::state::{Auction, AuctionParameters}; use solana_program_test::{tokio, ProgramTestContext}; use solana_sdk::transaction::TransactionError; use testing_engine::config::*; @@ -41,7 +46,7 @@ use utils::vaa::VaaArgs; #[tokio::test] pub async fn test_place_initial_offer_fallback() { let config = PlaceInitialOfferInstructionConfig::default(); - let (final_state, _) = place_initial_offer_fallback(config).await; + let (final_state, _, _) = Box::pin(place_initial_offer_shim(config, None)).await; assert_eq!( final_state .fast_market_order() @@ -63,20 +68,24 @@ pub async fn test_place_initial_offer_fallback() { #[tokio::test] pub async fn test_place_initial_offer_shimless() { let config = PlaceInitialOfferInstructionConfig::default(); - let (_final_state, _) = place_initial_offer_shimless(config).await; + let (_final_state, _, _) = Box::pin(place_initial_offer_shimless(config, None)).await; } /// Test that auction account is exactly the same when using shimless and fallback instructions #[tokio::test] pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { - let config = PlaceInitialOfferInstructionConfig { + let shimless_config = PlaceInitialOfferInstructionConfig { + actor: TestingActorEnum::Owner, + ..PlaceInitialOfferInstructionConfig::default() + }; + let fallback_config = PlaceInitialOfferInstructionConfig { actor: TestingActorEnum::Owner, ..PlaceInitialOfferInstructionConfig::default() }; - let (final_state_shimless, mut shimless_test_context) = - place_initial_offer_shimless(config.clone()).await; - let (final_state_fallback, mut fallback_test_context) = - place_initial_offer_fallback(config.clone()).await; + let (final_state_shimless, mut shimless_test_context, _) = + Box::pin(place_initial_offer_shimless(shimless_config, None)).await; + let (final_state_fallback, mut fallback_test_context, _) = + Box::pin(place_initial_offer_shim(fallback_config, None)).await; let shimless_auction = { let shimless_active_auction_address = final_state_shimless @@ -111,74 +120,51 @@ pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { compare_auctions(&shimless_auction, &shimful_auction).await; } -pub async fn place_initial_offer_fallback( - config: PlaceInitialOfferInstructionConfig, -) -> (TestingEngineState, ProgramTestContext) { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - - let testing_engine = TestingEngine::new(testing_context).await; - - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(config), - ]; - - ( - testing_engine - .execute(&mut test_context, instruction_triggers) - .await, - test_context, - ) -} - -pub async fn place_initial_offer_shimless( - config: PlaceInitialOfferInstructionConfig, -) -> (TestingEngineState, ProgramTestContext) { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShimless(config), - ]; - ( - testing_engine - .execute(&mut test_context, instruction_triggers) - .await, - test_context, - ) +/// Test place initial offer shim and then improve the offer (shimless) +#[tokio::test] +pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { + let config = PlaceInitialOfferInstructionConfig::default(); + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless(config, None)).await; + let improve_offer_config = ImproveOfferInstructionConfig::default(); + let instruction_triggers = vec![InstructionTrigger::ImproveOfferShimless( + improve_offer_config, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; } /* - Sad path tests + Sad path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** ** +** ** +** ** + ** ************ ** + ** ****** ****** ** + *** ***** ***** *** + ** *** *** ** + *** ** ** *** + **** **** + **** **** + ****** ****** + ***************** */ /// Test that the shimless place initial offer instruction blocks the shim instruction @@ -219,7 +205,7 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { }), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -262,10 +248,47 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } +/// Test with usdt token account +#[tokio::test] +pub async fn test_place_initial_shim_offer_usdt_token_account() { + let expected_error = ExpectedError { + instruction_index: 0, + error_code: 3, // Token spl transfer error code when mint does not match + error_string: "Invalid argument".to_string(), + }; + let config = PlaceInitialOfferInstructionConfig { + spl_token_enum: SplTokenEnum::Usdt, + expected_error: Some(expected_error), + ..PlaceInitialOfferInstructionConfig::default() + }; + Box::pin(place_initial_offer_shim(config, None)).await; +} + +/// Test with usdt token account as custom account +#[tokio::test] +pub async fn test_place_initial_shim_offer_usdt_mint_address() { + let custom_accounts = PlaceInitialOfferCustomAccounts { + mint_address: Some(crate::utils::constants::USDT_MINT), + ..PlaceInitialOfferCustomAccounts::default() + }; + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::InvalidMint), // Token spl transfer error code when mint does not match + error_string: "Invalid mint".to_string(), + }; + let config = PlaceInitialOfferInstructionConfig { + custom_accounts: Some(custom_accounts), + spl_token_enum: SplTokenEnum::Usdt, + expected_error: Some(expected_error), + ..PlaceInitialOfferInstructionConfig::default() + }; + Box::pin(place_initial_offer_shim(config, None)).await; +} + /// Test that the place initial offer fails if the fast market order is not created #[tokio::test] pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { @@ -307,6 +330,470 @@ pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { let testing_engine = TestingEngine::new(testing_context).await; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) + .await; +} + +/// Place initial offer shim fails when Offer > Max fee +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_when_offer_greater_than_max_fee() { + let amount_in = 123456789_u64; + let (vaa_args, mut initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_add(1), + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::OfferPriceTooHigh), + error_string: "Offer price is greater than max fee".to_string(), + }; + initial_offer_config.expected_error = Some(expected_error); + Box::pin(place_initial_offer_shim( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Place initial offer shim fails when amount in is u64::max +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_when_amount_in_is_u64_max() { + let amount_in = u64::MAX; + let (vaa_args, mut initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(i32::MAX), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::U64Overflow), + error_string: "U64Overflow".to_string(), + }; + initial_offer_config.expected_error = Some(expected_error); + Box::pin(place_initial_offer_shim( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Place initial offer shim fails when max fee and amount in sum to u64::max +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_when_max_fee_and_amount_in_sum_to_u64_max() { + let amount_in = u64::MAX.saturating_div(2).saturating_add(1); + let (vaa_args, mut initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(2), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_div(2), + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::U64Overflow), + error_string: "U64Overflow".to_string(), + }; + initial_offer_config.expected_error = Some(expected_error); + + Box::pin(place_initial_offer_shim( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Test that improved offer fails when improvement is too small +#[tokio::test] +pub async fn test_improve_offer_shim_fails_carping() { + let amount_in = 123456789_u64; + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + let (initial_offer_state, mut test_context, testing_engine) = Box::pin( + place_initial_offer_shim(initial_offer_config, Some(vaa_args)), + ) + .await; + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::CarpingNotAllowed), + error_string: "Carping not allowed".to_string(), + }; + + let improve_offer_config = ImproveOfferInstructionConfig { + offer_price: amount_in.saturating_sub(1), + expected_error: Some(expected_error), + ..ImproveOfferInstructionConfig::default() + }; + let instruction_triggers = vec![InstructionTrigger::ImproveOfferShimless( + improve_offer_config, + )]; + + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initial_offer_state), + ) .await; } + +/// Test that improved offer fails when improvement is too small after an allowed improvement +#[tokio::test] +pub async fn test_improve_offer_shim_fails_carping_second_improvement() { + let amount_in = 123456789_u64; + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + let (initial_offer_state, mut test_context, testing_engine) = Box::pin( + place_initial_offer_shim(initial_offer_config, Some(vaa_args)), + ) + .await; + let new_offer_price = amount_in.saturating_sub(1).saturating_div(2); + let improve_offer_config = ImproveOfferInstructionConfig { + offer_price: new_offer_price, + expected_error: None, + ..ImproveOfferInstructionConfig::default() + }; + let expected_error = ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::CarpingNotAllowed), + error_string: "Carping not allowed".to_string(), + }; + let improve_offer_config_2 = ImproveOfferInstructionConfig { + offer_price: new_offer_price.saturating_sub(1), + expected_error: Some(expected_error), + ..ImproveOfferInstructionConfig::default() + }; + let instruction_triggers = vec![ + InstructionTrigger::ImproveOfferShimless(improve_offer_config), + InstructionTrigger::ImproveOfferShimless(improve_offer_config_2), + ]; + + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initial_offer_state), + ) + .await; +} + +/* + Edge case tests section + 88 + 88 + 88 + ,adPPYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYb,88 +a8" "" a8P_____88 88P' `"8a I8[ "" a8" "8a 88P' "Y8 a8P_____88 a8" `Y88 +8b 8PP""""""" 88 88 `"Y8ba, 8b d8 88 8PP""""""" 8b 88 +"8a, ,aa "8b, ,aa 88 88 aa ]8I "8a, ,a8" 88 "8b, ,aa "8a, ,d88 + `"Ybbd8"' `"Ybbd8"' 88 88 `"YbbdP"' `"YbbdP"' 88 `"Ybbd8"' `"8bbdP"Y8 +*/ + +/// Test place initial offer shim when Offer == Max fee; Max fee == Amount in minus 1 +#[tokio::test] +pub async fn test_place_initial_offer_shim_when_offer_equals_max_fee() { + let amount_in = 123456789_u64; + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), // Equal to amount in in minus 1 + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), // Equal to max fee + post_vaa: false, + } + .create_vaa_args_and_initial_offer_config(); + + Box::pin(place_initial_offer_shim( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Test place initial offer shimless when Offer == Max fee; Max fee == Amount in minus 1 +#[tokio::test] +pub async fn test_place_initial_offer_shimless_when_offer_equals_max_fee() { + let amount_in = 123456789_u64; + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), // Equal to amount in in minus 1 + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from(111111111), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), // Equal to max fee + post_vaa: true, + } + .create_vaa_args_and_initial_offer_config(); + + Box::pin(place_initial_offer_shimless( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Test place initial offer shim when deposit amount == u256::MAX +#[tokio::test] +pub async fn test_place_initial_offer_shim_when_deposit_amount_is_u256_max() { + let amount_in = 123456789_u64; + let be_deposit_bytes: [u8; 32] = [ + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + ] + .concat() + .try_into() + .unwrap(); + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from_be_bytes(be_deposit_bytes), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), + post_vaa: true, + } + .create_vaa_args_and_initial_offer_config(); + + Box::pin(place_initial_offer_shim( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +/// Test place initial offer shimless when deposit amount == u256::MAX +#[tokio::test] +pub async fn test_place_initial_offer_shimless_when_deposit_amount_is_u256_max() { + let amount_in = 123456789_u64; + let be_deposit_bytes: [u8; 32] = [ + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + u64::MAX.to_be_bytes(), + ] + .concat() + .try_into() + .unwrap(); + let (vaa_args, initial_offer_config) = TestAuctionSetup { + amount_in, + min_amount_out: amount_in.saturating_sub(5), + max_fee: amount_in.saturating_sub(1), + init_auction_fee: amount_in.saturating_div(3), + deposit_amount: ruint::aliases::U256::from_be_bytes(be_deposit_bytes), + deposit_base_fee: amount_in.saturating_div(4), + offer_price: amount_in.saturating_sub(1), + post_vaa: true, + } + .create_vaa_args_and_initial_offer_config(); + + Box::pin(place_initial_offer_shimless( + initial_offer_config, + Some(vaa_args), + )) + .await; +} + +#[tokio::test] +pub async fn test_improve_offer_after_close_fast_market_order() { + let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin( + place_initial_offer_shim(PlaceInitialOfferInstructionConfig::default(), None), + ) + .await; + let instruction_triggers = vec![ + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + +/* +================================================================================ +Helper structs and functions +================================================================================ +*/ + +pub async fn place_initial_offer_shim( + config: PlaceInitialOfferInstructionConfig, + vaa_args: Option, +) -> (TestingEngineState, ProgramTestContext, TestingEngine) { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }); + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(config), + ]; + + ( + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await, + test_context, + testing_engine, + ) +} + +pub async fn place_initial_offer_shimless( + config: PlaceInitialOfferInstructionConfig, + vaa_args: Option, +) -> (TestingEngineState, ProgramTestContext, TestingEngine) { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }); + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(config), + ]; + ( + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await, + test_context, + testing_engine, + ) +} + +/// A struct representing the auction info and its valid state +// TODO: Use this or something similar to fuzz test over various initial offers. +#[derive(Clone)] +pub struct TestAuctionSetup { + pub amount_in: u64, // Must be small enough for security deposit to be less than u64::MAX + pub min_amount_out: u64, // Not used for anything can be any value + pub max_fee: u64, // Must be greater than or equal to offer price + pub init_auction_fee: u64, // Must be less than or equal to max fee + pub deposit_amount: ruint::aliases::U256, + pub deposit_base_fee: u64, + pub offer_price: u64, // Must be less than or equal to max fee + pub post_vaa: bool, // Must be true for shimless tests +} + +impl TestAuctionSetup { + #[allow(dead_code)] + pub fn calculate_security_deposit_notional(&self) -> u64 { + let test_auction_parameters = AuctionParameters { + user_penalty_reward_bps: 250000, + initial_penalty_bps: 250000, + duration: 2, + grace_period: 5, + penalty_period: 10, + min_offer_delta_bps: 20000, + security_deposit_base: 4200000, + security_deposit_bps: 5000, + }; + + matching_engine::utils::auction::compute_notional_security_deposit( + &test_auction_parameters, + self.amount_in, + ) + } + + pub fn create_vaa_args_and_initial_offer_config( + &self, + ) -> (VaaArgs, PlaceInitialOfferInstructionConfig) { + let create_deposit_and_fast_transfer_params = CreateDepositAndFastTransferParams { + deposit_params: CreateDepositParams { + amount: self.deposit_amount, + base_fee: self.deposit_base_fee, + }, + fast_transfer_params: CreateFastTransferParams { + amount_in: self.amount_in, + min_amount_out: self.amount_in, + max_fee: self.max_fee, + init_auction_fee: self.init_auction_fee, + }, + }; + let vaa_args = VaaArgs { + post_vaa: self.post_vaa, + create_deposit_and_fast_transfer_params, + ..Default::default() + }; + let initial_offer_config = PlaceInitialOfferInstructionConfig { + offer_price: self.offer_price, + ..PlaceInitialOfferInstructionConfig::default() + }; + (vaa_args, initial_offer_config) + } +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index 1d9b9e307..cc4e7055a 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -1,9 +1,9 @@ #![allow(clippy::expect_used)] #![allow(clippy::panic)] -//! # Make offer instruction testing +//! # Prepare order response instruction testing //! -//! This module contains tests for the place initial offer and improve offer instructions. +//! This module contains tests for the prepare order response instructions. //! //! ## Test Cases //! @@ -12,6 +12,11 @@ //! - `test_prepare_order_shim_fallback` - Test that the prepare order shim fallback instruction works correctly //! - `test_prepare_order_shimless` - Test that the prepare order shimless instruction works correctly //! +//! ### Sad path tests +//! +//! - `test_prepare_order_response_shimless_blocks_shimful` - Test that the prepare order response shimless instruction blocks the shimful instruction +//! - `test_prepare_order_response_shimful_blocks_shimless` - Test that the prepare order response shimful instruction blocks the shimless instruction +//! use crate::testing_engine; use crate::testing_engine::config::{ @@ -54,7 +59,7 @@ pub async fn test_prepare_order_shim_fallback() { InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -86,7 +91,7 @@ pub async fn test_prepare_order_shimless() { InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -131,7 +136,7 @@ pub async fn test_prepare_order_response_shimless_blocks_shimful() { }), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } @@ -170,6 +175,6 @@ pub async fn test_prepare_order_response_shimful_blocks_shimless() { }), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index 77e24f5aa..3fa99e568 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -50,6 +50,6 @@ pub async fn test_settle_auction_complete() { InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), ]; testing_engine - .execute(&mut test_context, instruction_triggers) + .execute(&mut test_context, instruction_triggers, None) .await; } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 0e783e298..57b541b36 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -16,11 +16,17 @@ use std::{collections::HashSet, rc::Rc}; -use crate::{shimless::initialize::AuctionParametersConfig, utils::Chain}; +use crate::{ + shimless::initialize::AuctionParametersConfig, + utils::{token_account::SplTokenEnum, Chain}, +}; use anchor_lang::prelude::*; use solana_sdk::signature::Keypair; -use super::setup::{TestingActor, TestingActors}; +use super::{ + setup::{TestingActor, TestingActors}, + state::TestingEngineState, +}; /// An instruction config contains the configuration arguments for an instruction as well as the expected error pub trait InstructionConfig: Default { @@ -38,6 +44,7 @@ pub type OverwriteCurrentState = Option; /// * `instruction_index` - The index of the instruction that is expected to error /// * `error_code` - The error code that is expected to be returned /// * `error_string` - A description of the error that is expected to be returned for debugging purposes +// TODO: Change the error string to either be checked for or change the field name AND make it optional #[derive(Clone)] pub struct ExpectedError { pub instruction_index: u8, @@ -209,23 +216,85 @@ impl Default for TestingActorEnum { } } -#[derive(Clone)] +#[derive(Clone, Default)] +pub struct PlaceInitialOfferCustomAccounts { + pub fast_market_order_address: Option, + pub offer_token_address: Option, + pub auction_config_address: Option, + pub from_router_endpoint: Option, + pub to_router_endpoint: Option, + pub custodian_address: Option, + pub mint_address: Option, + pub system_program_address: Option, + pub token_program_address: Option, +} + pub struct PlaceInitialOfferInstructionConfig { pub actor: TestingActorEnum, + pub test_vaa_pair_index: usize, pub offer_price: u64, pub payer_signer: Option>, pub fast_market_order_address: OverwriteCurrentState, + pub custom_accounts: OverwriteCurrentState, + pub spl_token_enum: SplTokenEnum, pub expected_error: Option, pub expected_log_messages: Option>, } +impl PlaceInitialOfferInstructionConfig { + pub fn get_from_and_to_router_endpoints( + &self, + current_state: &TestingEngineState, + ) -> (Pubkey, Pubkey) { + match &self.custom_accounts { + Some(custom_accounts) => { + let from_router_endpoint = match custom_accounts.from_router_endpoint { + Some(from_router_endpoint) => from_router_endpoint, + None => { + current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses( + current_state.base().transfer_direction, + ) + .0 + } + }; + let to_router_endpoint = match custom_accounts.to_router_endpoint { + Some(to_router_endpoint) => to_router_endpoint, + None => { + current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses( + current_state.base().transfer_direction, + ) + .1 + } + }; + (from_router_endpoint, to_router_endpoint) + } + None => current_state + .router_endpoints() + .expect("Router endpoints are not initialized") + .endpoints + .get_from_and_to_endpoint_addresses(current_state.base().transfer_direction), + } + } +} + impl Default for PlaceInitialOfferInstructionConfig { fn default() -> Self { Self { actor: TestingActorEnum::Solver(0), + test_vaa_pair_index: 0, offer_price: 1__000_000, payer_signer: None, fast_market_order_address: None, + custom_accounts: None, + spl_token_enum: SplTokenEnum::Usdc, expected_error: None, expected_log_messages: None, } @@ -242,7 +311,7 @@ impl InstructionConfig for PlaceInitialOfferInstructionConfig { } pub struct ImproveOfferInstructionConfig { - pub solver_index: usize, + pub actor: TestingActorEnum, pub offer_price: u64, pub payer_signer: Option>, pub expected_error: Option, @@ -252,7 +321,7 @@ pub struct ImproveOfferInstructionConfig { impl Default for ImproveOfferInstructionConfig { fn default() -> Self { Self { - solver_index: 0, + actor: TestingActorEnum::Solver(0), offer_price: 500_000, payer_signer: None, expected_error: None, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 21d72b768..a19bdeb3d 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -37,6 +37,7 @@ use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; use crate::testing_engine::setup::ShimMode; use crate::utils::auction::AuctionState; +use crate::utils::token_account::SplTokenEnum; use crate::utils::vaa::TestVaaPairs; use crate::utils::{ auction::{ @@ -174,8 +175,9 @@ impl TestingEngine { &self, test_context: &mut ProgramTestContext, instruction_chain: Vec, + initial_state: Option, ) -> TestingEngineState { - let mut current_state = self.create_initial_state(); + let mut current_state = initial_state.unwrap_or_else(|| self.create_initial_state()); self.verify_triggers(&instruction_chain); @@ -308,7 +310,7 @@ impl TestingEngine { testing_actors.owner_assistant.pubkey(), testing_actors .fee_recipient - .token_account_address() + .token_account_address(&SplTokenEnum::Usdc) .unwrap(), ) }; @@ -428,6 +430,10 @@ impl TestingEngine { fast_market_order_address: fast_market_order_account, fast_market_order_bump, fast_market_order, + close_account_refund_recipient: Pubkey::try_from_slice( + &fast_market_order.close_account_refund_recipient, + ) + .unwrap(), }, guardian_set_state: GuardianSetState { guardian_set_address: guardian_signature_info.guardian_set_pubkey, @@ -487,50 +493,18 @@ impl TestingEngine { current_state.router_endpoints().is_some(), "Router endpoints are not created" ); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); - let solver = config.actor.get_actor(&self.testing_context.testing_actors); - let expected_error = config.expected_error(); - let fast_vaa = ¤t_state - .base() - .vaas - .first() - .expect("Failed to get vaa pair") - .fast_transfer_vaa; - let fast_vaa_pubkey = fast_vaa.get_vaa_pubkey(); - let auction_config_address = current_state - .initialized() - .expect("Testing state is not initialized") - .auction_config_address; - let custodian_address = current_state - .initialized() - .expect("Testing state is not initialized") - .custodian_address; - let auction_accounts = AuctionAccounts::new( - Some(fast_vaa_pubkey), - solver.clone(), - auction_config_address, - ¤t_state - .router_endpoints() - .expect("Router endpoints are not created") - .endpoints, - custodian_address, - self.testing_context.get_usdc_mint_address(), - current_state.base().transfer_direction, - ); - let auction_state = shimless::make_offer::place_initial_offer_shimless( + + let initial_offer_placed_state = shimless::make_offer::place_initial_offer_shimless( &self.testing_context, test_context, - &auction_accounts, - fast_vaa, - config.offer_price, - &payer_signer, - expected_error, + current_state, + config, ) .await; - if expected_error.is_none() { + if config.expected_error().is_none() { + let initial_offer_placed_state = initial_offer_placed_state.unwrap(); + let auction_state = initial_offer_placed_state.auction_state; + let auction_accounts = initial_offer_placed_state.auction_accounts; auction_state .get_active_auction() .unwrap() @@ -557,13 +531,7 @@ impl TestingEngine { config: &ImproveOfferInstructionConfig, ) -> TestingEngineState { let expected_error = config.expected_error(); - let solver = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index"); - let offer_price = config.offer_price; + let actor = config.actor.get_actor(&self.testing_context.testing_actors); let payer_signer = config .payer_signer .clone() @@ -571,8 +539,8 @@ impl TestingEngine { let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, test_context, - solver.clone(), - offer_price, + &actor, + config, &payer_signer, current_state.auction_state(), expected_error, @@ -598,49 +566,12 @@ impl TestingEngine { current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, ) -> TestingEngineState { - let fast_market_order_address = config.fast_market_order_address.unwrap_or_else(|| { - current_state - .fast_market_order() - .expect("Fast market order is not created") - .fast_market_order_address - }); - let router_endpoints = current_state - .router_endpoints() - .expect("Router endpoints are not created"); - let solver = config.actor.get_actor(&self.testing_context.testing_actors); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); - let auction_config_address = current_state - .auction_config_address() - .expect("Auction config address not found"); - let custodian_address = current_state - .custodian_address() - .expect("Custodian address not found"); - let auction_accounts = AuctionAccounts::new( - None, - solver.clone(), - auction_config_address, - &router_endpoints.endpoints, - custodian_address, - self.testing_context.get_usdc_mint_address(), - current_state.base().transfer_direction, - ); - let fast_vaa_data = current_state - .get_first_test_vaa_pair() - .fast_transfer_vaa - .get_vaa_data(); let place_initial_offer_shim_fixture = shimful::shims_make_offer::place_initial_offer_fallback( &self.testing_context, test_context, - &payer_signer, - fast_vaa_data, - solver, - &fast_market_order_address, - &auction_accounts, - config.offer_price, + current_state, + config, config.expected_error(), ) .await; @@ -654,6 +585,7 @@ impl TestingEngine { .verify_auction(&self.testing_context, test_context) .await .expect("Could not verify auction"); + let auction_accounts = initial_offer_placed_state.auction_accounts; return TestingEngineState::InitialOfferPlaced { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), @@ -673,33 +605,17 @@ impl TestingEngine { current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); - - // TODO: Change to get auction accounts from current state - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - let fast_market_order_address = config.fast_market_order_address.unwrap_or_else(|| { - current_state - .fast_market_order() - .expect("Fast market order is not created") - .fast_market_order_address - }); - let active_auction_state = current_state - .auction_state() - .get_active_auction() - .expect("Active auction not found"); let result = shimful::shims_execute_order::execute_order_fallback_test( &self.testing_context, test_context, - auction_accounts, - &fast_market_order_address, - active_auction_state, - solver, - config.expected_error(), + current_state, + config, ) .await; if config.expected_error.is_none() { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); let order_executed_fallback_fixture = result.unwrap(); let order_executed_state = OrderExecutedState { cctp_message: order_executed_fallback_fixture.cctp_message, @@ -749,10 +665,11 @@ impl TestingEngine { .get_vaa_pubkey(), ), solver.actor.clone(), + current_state.close_account_refund_recipient(), auction_config_address, &router_endpoints.endpoints, custodian_address, - self.testing_context.get_usdc_mint_address(), + current_state.spl_token_enum().unwrap(), current_state.base().transfer_direction, ); let result = shimless::execute_order::execute_order_shimless_test( diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index c06778915..402e183e0 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -14,20 +14,19 @@ use crate::testing_engine::config::{ExpectedError, ExpectedLog}; use crate::utils::account_fixtures::FixtureAccounts; -use crate::utils::airdrop::airdrop; +use crate::utils::airdrop::{airdrop, airdrop_spl_token}; use crate::utils::mint::MintFixture; use crate::utils::program_fixtures::{ initialise_cctp_message_transmitter, initialise_cctp_token_messenger_minter, initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, initialise_verify_shims, initialise_wormhole_core_bridge, }; +use crate::utils::token_account::{ + create_token_account, read_keypair_from_file, SplTokenEnum, TokenAccountFixture, +}; use crate::utils::vaa::{ create_vaas_test_with_chain_and_address, ChainAndAddress, TestVaaPair, TestVaaPairs, VaaArgs, }; -use crate::utils::{ - airdrop::airdrop_usdc, - token_account::{create_token_account, read_keypair_from_file, TokenAccountFixture}, -}; use crate::utils::{Chain, REGISTERED_TOKEN_ROUTERS}; use anchor_lang::AccountDeserialize; use anchor_spl::token::{ @@ -52,17 +51,23 @@ cfg_if::cfg_if! { //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"); //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("HUXc7MBf55vWrrkevVbmJN8HAyfFtjLcPLBt9yWngKzm"); const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); + const USDT_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint.json"; + const USDT_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdt_mint.json"; } else if #[cfg(feature = "testnet")] { //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS"); //const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("6yKmqWarCry3c8ntYKzM4WiS2fVypxLbENE2fP8onJje"); const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + const USDT_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; + const USDT_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdt_mint.json"; } else if #[cfg(feature = "localnet")] { //const PROGRAM_ID : Pubkey = solana_sdk::pubkey!("MatchingEngine11111111111111111111111111111"); // const CCTP_MINT_RECIPIENT: Pubkey = solana_sdk::pubkey!("35iwWKi7ebFyXNaqpswd1g9e9jrjvqWPV39nCQPaBbX1"); const USDC_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + const USDT_MINT_ADDRESS: Pubkey = solana_sdk::pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); const USDC_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdc_mint_devnet.json"; + const USDT_MINT_FIXTURE_PATH: &str = "tests/fixtures/usdt_mint.json"; } } const OWNER_KEYPAIR_PATH: &str = "tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json"; @@ -188,12 +193,20 @@ impl TestingContext { .await; // Create USDC mint - let _mint_fixture = MintFixture::new_from_file(&mut test_context, USDC_MINT_FIXTURE_PATH); + let _usdc_mint_fixture = + MintFixture::new_from_file(&mut test_context, USDC_MINT_FIXTURE_PATH); + let _usdt_mint_fixture = + MintFixture::new_from_file(&mut test_context, USDT_MINT_FIXTURE_PATH); // Create USDC ATAs for all actors that need them pre_testing_context .testing_actors - .create_atas(&mut test_context, USDC_MINT_ADDRESS) + .create_usdc_atas(&mut test_context, USDC_MINT_ADDRESS) + .await; + + pre_testing_context + .testing_actors + .create_usdt_atas(&mut test_context, USDT_MINT_ADDRESS) .await; let vaa_pairs = match vaas_test { Some(vaas_test) => vaas_test, @@ -415,9 +428,13 @@ pub struct Solver { } impl Solver { - pub fn new(keypair: Rc, token_account: Option) -> Self { + pub fn new( + keypair: Rc, + usdc_token_account: Option, + usdt_token_account: Option, + ) -> Self { Self { - actor: TestingActor::new(keypair, token_account), + actor: TestingActor::new(keypair, usdc_token_account, usdt_token_account), } } @@ -430,7 +447,7 @@ impl Solver { } pub fn token_account_address(&self) -> Option { - self.actor.token_account.as_ref().map(|t| t.address) + self.actor.usdc_token_account.as_ref().map(|t| t.address) } /// Approves the USDC mint for the given delegate @@ -440,19 +457,26 @@ impl Solver { /// * `test_context` - The test context /// * `delegate` - The delegate to approve the USDC mint to /// * `amount` - The amount of USDC to approve - pub async fn approve_usdc( + pub async fn approve_spl_token( &self, test_context: &mut ProgramTestContext, delegate: &Pubkey, amount: u64, + spl_token_enum: &SplTokenEnum, ) { self.actor - .approve_usdc(test_context, delegate, amount) + .approve_spl_token(test_context, delegate, amount, spl_token_enum) .await; } - pub async fn get_token_account_balance(&self, test_context: &mut ProgramTestContext) -> u64 { - self.actor.get_token_account_balance(test_context).await + pub async fn get_token_account_balance( + &self, + test_context: &mut ProgramTestContext, + spl_token_enum: &SplTokenEnum, + ) -> u64 { + self.actor + .get_token_account_balance(test_context, spl_token_enum) + .await } pub async fn get_lamport_balance(&self, test_context: &mut ProgramTestContext) -> u64 { @@ -469,7 +493,8 @@ impl Solver { #[derive(Clone)] pub struct TestingActor { pub keypair: Rc, - pub token_account: Option, + pub usdc_token_account: Option, + pub usdt_token_account: Option, } impl std::fmt::Debug for TestingActor { @@ -478,16 +503,21 @@ impl std::fmt::Debug for TestingActor { f, "TestingActor {{ pubkey: {:?}, token_account: {:?} }}", self.keypair.pubkey(), - self.token_account + self.usdc_token_account ) } } impl TestingActor { - pub fn new(keypair: Rc, token_account: Option) -> Self { + pub fn new( + keypair: Rc, + usdc_token_account: Option, + usdt_token_account: Option, + ) -> Self { Self { keypair, - token_account, + usdc_token_account, + usdt_token_account, } } pub fn pubkey(&self) -> Pubkey { @@ -497,8 +527,11 @@ impl TestingActor { self.keypair.clone() } - pub fn token_account_address(&self) -> Option { - self.token_account.as_ref().map(|t| t.address) + pub fn token_account_address(&self, spl_token_enum: &SplTokenEnum) -> Option { + match spl_token_enum { + SplTokenEnum::Usdc => self.usdc_token_account.as_ref().map(|t| t.address), + SplTokenEnum::Usdt => self.usdt_token_account.as_ref().map(|t| t.address), + } } /// Gets the balance of the token account @@ -506,8 +539,12 @@ impl TestingActor { /// # Arguments /// /// * `test_context` - The test context - pub async fn get_token_account_balance(&self, test_context: &mut ProgramTestContext) -> u64 { - if let Some(token_account) = self.token_account_address() { + pub async fn get_token_account_balance( + &self, + test_context: &mut ProgramTestContext, + spl_token_enum: &SplTokenEnum, + ) -> u64 { + if let Some(token_account) = self.token_account_address(spl_token_enum) { let account = test_context .banks_client .get_account(token_account) @@ -536,11 +573,12 @@ impl TestingActor { /// * `test_context` - The test context /// * `delegate` - The delegate to approve the USDC mint to /// * `amount` - The amount of USDC to approve - pub async fn approve_usdc( + pub async fn approve_spl_token( &self, test_context: &mut ProgramTestContext, delegate: &Pubkey, amount: u64, + spl_token_enum: &SplTokenEnum, ) { // If signer pubkeys are empty, it means that the owner is the signer let last_blockhash = test_context @@ -549,7 +587,7 @@ impl TestingActor { .expect("Failed to get new blockhash"); let approve_ix = approve( &spl_token::ID, - &self.token_account_address().unwrap(), + &self.token_account_address(spl_token_enum).unwrap(), delegate, &self.pubkey(), &[], @@ -590,17 +628,17 @@ impl TestingActors { /// # Returns pub fn new(owner_keypair_path: &str) -> Self { let owner_kp = Rc::new(read_keypair_from_file(owner_keypair_path)); - let owner = TestingActor::new(owner_kp.clone(), None); - let owner_assistant = TestingActor::new(owner_kp.clone(), None); - let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None); - let relayer = TestingActor::new(Rc::new(Keypair::new()), None); + let owner = TestingActor::new(owner_kp.clone(), None, None); + let owner_assistant = TestingActor::new(owner_kp.clone(), None, None); + let fee_recipient = TestingActor::new(Rc::new(Keypair::new()), None, None); + let relayer = TestingActor::new(Rc::new(Keypair::new()), None, None); let mut solvers = vec![]; solvers.extend(vec![ - Solver::new(Rc::new(Keypair::new()), None), - Solver::new(Rc::new(Keypair::new()), None), - Solver::new(Rc::new(Keypair::new()), None), + Solver::new(Rc::new(Keypair::new()), None, None), + Solver::new(Rc::new(Keypair::new()), None, None), + Solver::new(Rc::new(Keypair::new()), None, None), ]); - let liquidator = TestingActor::new(Rc::new(Keypair::new()), None); + let liquidator = TestingActor::new(Rc::new(Keypair::new()), None, None); Self { owner, owner_assistant, @@ -636,7 +674,7 @@ impl TestingActors { } /// Set up ATAs for Various Owners - async fn create_atas( + async fn create_usdc_atas( &mut self, test_context: &mut ProgramTestContext, usdc_mint_address: Pubkey, @@ -644,8 +682,34 @@ impl TestingActors { for actor in self.token_account_actors() { let usdc_ata = create_token_account(test_context, &actor.keypair(), &usdc_mint_address).await; - airdrop_usdc(test_context, &usdc_ata.address, 420_000__000_000).await; - actor.token_account = Some(usdc_ata); + airdrop_spl_token( + test_context, + &usdc_ata.address, + 420_000__000_000, + usdc_mint_address, + ) + .await; + actor.usdc_token_account = Some(usdc_ata); + } + } + + /// Create usdt associated token accounts + pub async fn create_usdt_atas( + &mut self, + test_context: &mut ProgramTestContext, + usdt_mint_address: Pubkey, + ) { + for actor in self.token_account_actors() { + let usdt_ata = + create_token_account(test_context, &actor.keypair(), &usdt_mint_address).await; + airdrop_spl_token( + test_context, + &usdt_ata.address, + 420_000__000_000, + usdt_mint_address, + ) + .await; + actor.usdt_token_account = Some(usdt_ata); } } @@ -656,13 +720,15 @@ impl TestingActors { test_context: &mut ProgramTestContext, num_solvers: usize, usdc_mint_address: Pubkey, + usdt_mint_address: Pubkey, ) { for _ in 0..num_solvers { let keypair = Rc::new(Keypair::new()); let usdc_ata = create_token_account(test_context, &keypair, &usdc_mint_address).await; + let usdt_ata = create_token_account(test_context, &keypair, &usdt_mint_address).await; airdrop(test_context, &keypair.pubkey(), 10000000000).await; self.solvers - .push(Solver::new(keypair.clone(), Some(usdc_ata))); + .push(Solver::new(keypair.clone(), Some(usdc_ata), Some(usdt_ata))); } } } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 36d86c2fc..26645f262 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -17,6 +17,7 @@ use crate::utils::{ account_fixtures::FixtureAccounts, auction::{AuctionAccounts, AuctionState}, router::TestRouterEndpoints, + token_account::SplTokenEnum, vaa::{TestVaaPair, TestVaaPairs}, }; use anchor_lang::prelude::*; @@ -47,11 +48,13 @@ pub struct FastMarketOrderAccountCreatedState { pub fast_market_order_address: Pubkey, pub fast_market_order_bump: u8, pub fast_market_order: FastMarketOrder, + pub close_account_refund_recipient: Pubkey, } #[derive(Clone)] pub struct InitialOfferPlacedState { pub auction_state: AuctionState, + pub auction_accounts: AuctionAccounts, } #[derive(Clone)] @@ -254,6 +257,7 @@ impl TestingEngineState { Self::OrderExecuted { auction_state, .. } => auction_state, Self::OrderPrepared { auction_state, .. } => auction_state, Self::AuctionSettled { auction_state, .. } => auction_state, + Self::FastMarketOrderClosed { auction_state, .. } => auction_state, _ => &AuctionState::Inactive, } } @@ -304,4 +308,14 @@ impl TestingEngineState { pub fn auction_config_address(&self) -> Option { self.initialized().map(|state| state.auction_config_address) } + + pub fn spl_token_enum(&self) -> Option { + self.auction_accounts() + .map(|accounts| accounts.spl_token_enum.clone()) + } + + pub fn close_account_refund_recipient(&self) -> Option { + self.fast_market_order() + .map(|fast_market_order| fast_market_order.close_account_refund_recipient) + } } diff --git a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs index 8d6612d63..206c1ec65 100644 --- a/solana/modules/matching-engine-testing/tests/utils/airdrop.rs +++ b/solana/modules/matching-engine-testing/tests/utils/airdrop.rs @@ -3,8 +3,6 @@ use solana_program_test::ProgramTestContext; use solana_sdk::transaction::{Transaction, VersionedTransaction}; use solana_sdk::{pubkey::Pubkey, signature::Signer, system_instruction}; -use super::constants; - /// Airdrops SOL to a given recipient /// /// # Arguments @@ -38,19 +36,19 @@ pub async fn airdrop(test_context: &mut ProgramTestContext, recipient: &Pubkey, /// * `test_context` - The program test context /// * `recipient_ata` - The recipient's ATA /// * `amount` - The amount of USDC to airdrop -pub async fn airdrop_usdc( +pub async fn airdrop_spl_token( test_context: &mut ProgramTestContext, recipient_ata: &Pubkey, amount: u64, + mint_address: Pubkey, ) { let new_blockhash = test_context .get_new_latest_blockhash() .await .expect("Failed to get new blockhash"); - let usdc_mint_address = constants::USDC_MINT; let mint_to_ix = spl_token::instruction::mint_to( &spl_token::ID, - &usdc_mint_address, + &mint_address, recipient_ata, &test_context.payer.pubkey(), &[], diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index b50f0ff50..35127c674 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -1,8 +1,8 @@ use anchor_lang::prelude::*; use solana_program_test::ProgramTestContext; -use super::router::TestRouterEndpoints; use super::Chain; +use super::{router::TestRouterEndpoints, token_account::SplTokenEnum}; use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; use anyhow::{anyhow, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; @@ -23,12 +23,13 @@ use matching_engine::state::{Auction, AuctionInfo}; pub struct AuctionAccounts { pub posted_fast_vaa: Option, pub offer_token: Pubkey, - pub actor: TestingActor, + pub offer_actor: TestingActor, + pub close_account_refund_recipient: Option, // Only for shim pub auction_config: Pubkey, pub from_router_endpoint: Pubkey, pub to_router_endpoint: Pubkey, pub custodian: Pubkey, - pub usdc_mint: Pubkey, + pub spl_token_enum: SplTokenEnum, } /// An enum representing the state of an auction @@ -71,6 +72,7 @@ pub struct ActiveAuctionState { pub auction_config_address: Pubkey, pub initial_offer: AuctionOffer, pub best_offer: AuctionOffer, + pub spl_token_enum: SplTokenEnum, } /// A struct representing an auction offer @@ -88,13 +90,15 @@ pub struct AuctionOffer { } impl AuctionAccounts { + #[allow(clippy::too_many_arguments)] pub fn new( posted_fast_vaa: Option, - actor: TestingActor, + offer_actor: TestingActor, + close_account_refund_recipient: Option, auction_config: Pubkey, router_endpoints: &TestRouterEndpoints, custodian: Pubkey, - usdc_mint: Pubkey, + spl_token_enum: SplTokenEnum, direction: TransferDirection, ) -> Self { let (from_router_endpoint, to_router_endpoint) = match direction { @@ -116,13 +120,14 @@ impl AuctionAccounts { }; Self { posted_fast_vaa, - offer_token: actor.token_account_address().unwrap(), - actor, + offer_token: offer_actor.token_account_address(&spl_token_enum).unwrap(), + close_account_refund_recipient, + offer_actor, auction_config, from_router_endpoint, to_router_endpoint, custodian, - usdc_mint, + spl_token_enum, } } } diff --git a/solana/modules/matching-engine-testing/tests/utils/constants.rs b/solana/modules/matching-engine-testing/tests/utils/constants.rs index 076885674..1ad9d421f 100644 --- a/solana/modules/matching-engine-testing/tests/utils/constants.rs +++ b/solana/modules/matching-engine-testing/tests/utils/constants.rs @@ -55,6 +55,7 @@ cfg_if::cfg_if! { /// USDC mint address found on Solana mainnet. pub const USDC_MINT: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); + pub const USDT_MINT: Pubkey = pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); } else if #[cfg(feature = "testnet")] { /// Core Bridge program ID on Solana devnet. pub const CORE_BRIDGE_PID: Pubkey = pubkey!("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"); @@ -70,6 +71,7 @@ cfg_if::cfg_if! { /// USDC mint address found on Solana devnet. pub const USDC_MINT: Pubkey = pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + pub const USDT_MINT: Pubkey = pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); } else if #[cfg(feature = "localnet")] { /// Core Bridge program ID on Wormhole's Tilt (dev) network. pub const CORE_BRIDGE_PID: Pubkey = pubkey!("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); @@ -87,6 +89,7 @@ cfg_if::cfg_if! { /// /// NOTE: We expect an integrator to load this account by pulling it from Solana devnet. pub const USDC_MINT: Pubkey = pubkey!("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); + pub const USDT_MINT: Pubkey = pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); } } diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index d7fc8a15f..3d5099672 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -5,7 +5,7 @@ use super::constants::*; use super::token_account::create_token_account_for_pda; -use crate::testing_engine::setup::TestingContext; +use crate::testing_engine::setup::{TestingContext, TransferDirection}; use anchor_lang::prelude::*; use anchor_lang::{InstructionData, ToAccountMetas}; @@ -97,6 +97,28 @@ impl Deref for TestRouterEndpoints { } } +impl TestRouterEndpoints { + #[allow(dead_code)] + pub fn get_from_and_to_endpoint_addresses( + &self, + transfer_direction: TransferDirection, + ) -> (Pubkey, Pubkey) { + match transfer_direction { + TransferDirection::FromArbitrumToEthereum => ( + self.get(&Chain::Arbitrum).unwrap().endpoint_address, + self.get(&Chain::Ethereum).unwrap().endpoint_address, + ), + TransferDirection::FromEthereumToArbitrum => ( + self.get(&Chain::Ethereum).unwrap().endpoint_address, + self.get(&Chain::Arbitrum).unwrap().endpoint_address, + ), + TransferDirection::Other => { + panic!("Unsupported transfer direction"); + } + } + } +} + impl TestRouterEndpoints { #[allow(dead_code)] pub fn get_endpoint_info(&self, chain: Chain) -> TestEndpointInfo { diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index ea7c6e878..3821531a8 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -147,3 +147,10 @@ pub fn read_keypair_from_file(filename: &str) -> Keypair { // Create keypair from bytes Keypair::from_bytes(&bytes).expect("Bytes must form a valid keypair") } + +/// Enum representing the different SPL token types +#[derive(Clone)] +pub enum SplTokenEnum { + Usdc, + Usdt, +} diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index 489c6e8dd..0600b161e 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -297,14 +297,14 @@ impl CreateDepositAndFastTransferParams { } pub struct CreateDepositParams { - pub amount: i32, + pub amount: ruint::aliases::U256, pub base_fee: u64, } impl Default for CreateDepositParams { fn default() -> Self { Self { - amount: 69000000, + amount: ruint::aliases::U256::from(69000000), base_fee: 2, } } @@ -524,7 +524,7 @@ pub fn create_deposit_message( source_address: ChainAddress, _destination_address: ChainAddress, cctp_mint_recipient: Pubkey, - amount: i32, + amount: ruint::aliases::U256, base_fee: u64, test_vaa_args: &TestVaaArgs, ) -> (Pubkey, PostedVaaData, Deposit) { @@ -535,7 +535,7 @@ pub fn create_deposit_message( // Implements TypePrefixedPayload let deposit = Deposit { token_address: token_mint.to_bytes(), - amount: ruint::aliases::U256::from(amount), + amount, source_cctp_domain: source_address.chain.as_cctp_domain(), destination_cctp_domain: Chain::Solana.as_cctp_domain(), // Hardcode solana as destination domain cctp_nonce, @@ -590,7 +590,7 @@ pub fn create_fast_transfer_message( let start_timestamp = test_vaa_args.start_timestamp; let sequence = test_vaa_args.sequence; let vaa_nonce = test_vaa_args.vaa_nonce; - // If start timestamp is not provided, set the deadline to 0 + // If start timestamp is not provided, set the deadline to 0, otherwise set the deadline to 10 seconds from the start timestamp let deadline = start_timestamp .map(|timestamp| timestamp.saturating_add(10)) .unwrap_or_default(); diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 89d8e64e1..8993feef1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -54,11 +54,18 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { let fast_market_order = &accounts[0]; let close_account_refund_recipient = &accounts[1]; + // Check that the close_account_refund_recipient is a signer, otherwise someone might call this and steal the lamports if !close_account_refund_recipient.is_signer { msg!("Refund recipient (account #2) is not a signer"); return Err(ProgramError::InvalidAccountData.into()); } + // Check that the fast_market_order is owned by the close_account_refund_recipient + if fast_market_order.owner != &close_account_refund_recipient.key() { + msg!("Fast market order is not owned by the close account refund recipient"); + return Err(ErrorCode::ConstraintOwner.into()); + } + let fast_market_order_data = FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; if fast_market_order_data.close_account_refund_recipient diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 83e2e91ec..d88232d65 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -114,8 +114,6 @@ pub fn initialise_fast_market_order( let fast_market_order_account = &accounts[1]; let guardian_set = &accounts[2]; let guardian_set_signatures = &accounts[3]; - let _verify_vaa_shim_program = &accounts[4]; - let _system_program = &accounts[5]; let InitialiseFastMarketOrderData { fast_market_order, @@ -124,7 +122,7 @@ pub fn initialise_fast_market_order( } = *data; // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ - let fast_market_order_digest = fast_market_order.digest(); + let fast_market_order_vaa_digest = fast_market_order.digest(); // Did not want to pass in the vaa hash here. So recreated it. let verify_hash_data = { let mut data = vec![]; @@ -132,11 +130,11 @@ pub fn initialise_fast_market_order( &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, ); data.push(guardian_set_bump); - data.extend_from_slice(&fast_market_order_digest); + data.extend_from_slice(&fast_market_order_vaa_digest); data }; let verify_shim_ix = Instruction { - program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, // Because program is hardcoded, the check is not needed. accounts: vec![ AccountMeta::new_readonly(guardian_set.key(), false), AccountMeta::new_readonly(guardian_set_signatures.key(), false), @@ -162,7 +160,7 @@ pub fn initialise_fast_market_order( let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrderState::SEED_PREFIX, - fast_market_order_digest.as_ref(), + fast_market_order_vaa_digest.as_ref(), fast_market_order.close_account_refund_recipient.as_ref(), ], &program_id, @@ -175,7 +173,7 @@ pub fn initialise_fast_market_order( } let fast_market_order_seeds = [ FastMarketOrderState::SEED_PREFIX, - fast_market_order_digest.as_ref(), + fast_market_order_vaa_digest.as_ref(), fast_market_order.close_account_refund_recipient.as_ref(), &[fast_market_order_bump], ]; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 3b3194493..46a989afd 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -325,7 +325,7 @@ pub fn place_initial_offer_cctp_shim( { let deadline = i64::from(fast_market_order_zero_copy.deadline); let expiration = i64::from(vaa_time).saturating_add(crate::VAA_AUCTION_EXPIRATION_TIME); - let current_time = Clock::get().unwrap().unix_timestamp; + let current_time: i64 = Clock::get().unwrap().unix_timestamp; if !((deadline == 0 || current_time < deadline) && current_time < expiration) { msg!("Fast market order has expired"); return Err(MatchingEngineError::FastMarketOrderExpired.into()); diff --git a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs index c2c3c6ee6..8d3a5e604 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/improve.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/improve.rs @@ -32,7 +32,7 @@ pub struct ImproveOffer<'info> { require!( offer_price - < utils::auction::compute_min_allowed_offer(&active_auction.config, info), + < utils::auction::compute_max_allowed_offer(&active_auction.config, info), MatchingEngineError::CarpingNotAllowed ); @@ -148,7 +148,7 @@ pub fn improve_offer(ctx: Context, offer_price: u64) -> Result<()> token_balance_before: offer_token.amount, amount_in: info.amount_in, total_deposit: info.total_deposit(), - max_offer_price_allowed: utils::auction::compute_min_allowed_offer(config, info) + max_offer_price_allowed: utils::auction::compute_max_allowed_offer(config, info) .checked_sub(1), })); } diff --git a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs index 44f301b0f..3bc3cae5b 100644 --- a/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/offer/place_initial/cctp.rs @@ -180,7 +180,7 @@ pub fn place_initial_offer_cctp( token_balance_before: ctx.accounts.offer_token.amount, amount_in, total_deposit: info.total_deposit(), - max_offer_price_allowed: utils::auction::compute_min_allowed_offer(config, info) + max_offer_price_allowed: utils::auction::compute_max_allowed_offer(config, info) .checked_sub(1), })); diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index d9e98332e..36c8d60e7 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -107,7 +107,7 @@ impl FastMarketOrder { /// Creates an payload as expected in a fast market order vaa pub fn payload(&self) -> Vec { let mut payload = vec![]; - payload.push(11_u8); + payload.push(11_u8); // TODO: Explain why this is 11 payload.extend_from_slice(&self.amount_in.to_be_bytes()); payload.extend_from_slice(&self.min_amount_out.to_be_bytes()); payload.extend_from_slice(&self.target_chain.to_be_bytes()); diff --git a/solana/programs/matching-engine/src/utils/auction.rs b/solana/programs/matching-engine/src/utils/auction.rs index 3ebd5c0a2..a7476199d 100644 --- a/solana/programs/matching-engine/src/utils/auction.rs +++ b/solana/programs/matching-engine/src/utils/auction.rs @@ -51,7 +51,7 @@ pub fn compute_deposit_penalty( } #[inline] -pub fn compute_min_allowed_offer(params: &AuctionParameters, info: &AuctionInfo) -> u64 { +pub fn compute_max_allowed_offer(params: &AuctionParameters, info: &AuctionInfo) -> u64 { info.offer_price .saturating_sub(mul_bps_unsafe(info.offer_price, params.min_offer_delta_bps)) } @@ -341,7 +341,7 @@ mod test { let offer_price = 10000000; let (info, _) = set_up(0, None, offer_price); - let allowed_offer = compute_min_allowed_offer(¶ms, &info); + let allowed_offer = compute_max_allowed_offer(¶ms, &info); assert_eq!(allowed_offer, 0); } @@ -353,7 +353,7 @@ mod test { let offer_price = 10000000; let (info, _) = set_up(0, None, offer_price); - let allowed_offer = compute_min_allowed_offer(¶ms, &info); + let allowed_offer = compute_max_allowed_offer(¶ms, &info); assert_eq!(allowed_offer, offer_price); } @@ -364,7 +364,7 @@ mod test { let offer_price = 10000000; let (info, _) = set_up(0, None, offer_price); - let allowed_offer = compute_min_allowed_offer(¶ms, &info); + let allowed_offer = compute_max_allowed_offer(¶ms, &info); assert_eq!(allowed_offer, offer_price - 500000); } From c0ddd2a13ae76bfd9f1ad855224b4843ed334fc8 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 7 Apr 2025 21:57:31 +0100 Subject: [PATCH 056/112] more tests added --- .../create_and_close_fast_market_order.rs | 166 +++++++++++ .../tests/test_scenarios/execute_order.rs | 257 +++++++++++++++--- .../tests/test_scenarios/make_offer.rs | 168 ++++++++++-- .../tests/test_scenarios/prepare_order.rs | 217 ++++++++++++++- .../tests/testing_engine/engine.rs | 201 ++++++++++---- .../tests/testing_engine/state.rs | 6 + .../tests/utils/auction.rs | 41 ++- 7 files changed, 918 insertions(+), 138 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs index fdb307d90..ce8098014 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs @@ -221,6 +221,42 @@ pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_in .await; } +/// Test that the same fast market order cannot be closed twice +#[tokio::test] +pub async fn test_fast_market_order_cannot_be_closed_twice() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 3001, + error_string: "Fast market order account is already closed".to_string(), + }), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; +} /* Edge case tests section 88 @@ -232,3 +268,133 @@ a8" "" a8P_____88 88P' `"8a I8[ "" a8" "8a 88P' "Y8 a8P_____88 a8 "8a, ,aa "8b, ,aa 88 88 aa ]8I "8a, ,a8" 88 "8b, ,aa "8a, ,d88 `"Ybbd8"' `"Ybbd8"' 88 88 `"YbbdP"' `"YbbdP"' 88 `"Ybbd8"' `"8bbdP"Y8 */ + +/// Test that fast market order can be opened after being closed by the same solver +#[tokio::test] +pub async fn test_fast_market_order_can_be_opened_after_being_closed_by_the_same_solver() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; +} + +/// Test that multiple fast market orders can be opened and closed by different solvers in arbitrary order +#[tokio::test] +pub async fn test_multiple_fast_market_orders_can_be_opened_and_closed_by_different_solvers_in_arbitrary_order( +) { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let solver_1 = testing_context.testing_actors.solvers[1].clone(); + let solver_2 = testing_context.testing_actors.solvers[2].clone(); + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 0, + close_account_refund_recipient: None, // Solver 0 + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + ]; + let current_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + let fast_market_order_0_pubkey = current_state + .fast_market_order() + .unwrap() + .fast_market_order_address; + let instruction_triggers_1 = vec![InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 1, + close_account_refund_recipient: Some(solver_1.pubkey()), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + )]; + let current_state = testing_engine + .execute( + &mut test_context, + instruction_triggers_1, + Some(current_state), + ) + .await; + let fast_market_order_1_pubkey = current_state + .fast_market_order() + .unwrap() + .fast_market_order_address; + let instruction_triggers_2 = vec![ + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + fast_market_order_address: Some(fast_market_order_0_pubkey), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 2, + close_account_refund_recipient: Some(solver_2.pubkey()), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + ]; + let current_state = testing_engine + .execute( + &mut test_context, + instruction_triggers_2, + Some(current_state), + ) + .await; + let fast_market_order_2_pubkey = current_state + .fast_market_order() + .unwrap() + .fast_market_order_address; + let instruction_triggers_3 = vec![ + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some(solver_2.keypair()), + fast_market_order_address: Some(fast_market_order_2_pubkey), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + InstructionTrigger::CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some(solver_1.keypair()), + fast_market_order_address: Some(fast_market_order_1_pubkey), + ..CloseFastMarketOrderShimInstructionConfig::default() + }), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers_3, + Some(current_state), + ) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 09431018c..893c6dcfb 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -9,75 +9,150 @@ //! - `test_execute_order_shimless` - Test that the execute order shimless instruction works correctly //! +use crate::test_scenarios::make_offer::place_initial_offer_shimless; use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; +use crate::testing_engine::engine::{ExecutionChain, ExecutionTrigger, VerificationTrigger}; +use crate::testing_engine::state::TestingEngineState; use crate::utils; -use solana_program_test::tokio; +use solana_program_test::{tokio, ProgramTestContext}; use testing_engine::config::*; use testing_engine::engine::{InstructionTrigger, TestingEngine}; use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; use utils::vaa::VaaArgs; -/// Test that the execute order fallback instruction works correctly +use super::make_offer::place_initial_offer_shim; + +/// Test that the execute order shim instruction works correctly #[tokio::test] // TODO: Flesh out this test to see if the message was posted correctly -pub async fn test_execute_order_fallback() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: false, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, +pub async fn test_execute_order_shim() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + execute_order_helper( + ExecuteOrderInstructionConfig::default(), + ShimExecutionMode::Shim, + None, transfer_direction, - Some(vaa_args), ) .await; - let testing_engine = TestingEngine::new(testing_context).await; +} + +/// Test that the execute order shimless instruction works correctly +#[tokio::test] +pub async fn test_execute_order_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + execute_order_helper( + ExecuteOrderInstructionConfig::default(), + ShimExecutionMode::Shimless, + None, + transfer_direction, + ) + .await; +} + +/// Test that reopening fast market order account and then executing order succeeds +#[tokio::test] +pub async fn test_execute_order_after_reopening_fast_market_order_account() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let testing_actors = &testing_engine.testing_context.testing_actors; + // Get the second solver because the first one was used to set up the initial fast market order account + let close_account_refund_recipient = testing_actors.solvers.get(1).unwrap().pubkey(); let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), ), InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 1, + close_account_refund_recipient: Some(close_account_refund_recipient), + payer_signer: None, + expected_error: None, + expected_log_messages: None, + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; - testing_engine - .execute(&mut test_context, instruction_triggers, None) + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification( + VerificationTrigger::VerifyAuctionState(true), + )); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) .await; } -/// Test that the execute order shimless instruction works correctly +/// Test execute order shim after placing initial offer with shimless instruction #[tokio::test] -pub async fn test_execute_order_shimless() { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() - }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; +pub async fn test_execute_order_shim_after_placing_initial_offer_with_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; let instruction_triggers = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), ), - InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), - InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; - testing_engine - .execute(&mut test_context, instruction_triggers, None) + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification( + VerificationTrigger::VerifyAuctionState(true), + )); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) + .await; +} + +/// Test execute order shimless after placing initial offer with shim instruction +#[tokio::test] +pub async fn test_execute_order_shimless_after_placing_initial_offer_with_shim() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + Some(VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }), + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + ExecuteOrderInstructionConfig::default(), + )]; + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification( + VerificationTrigger::VerifyAuctionState(true), + )); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) .await; } @@ -109,9 +184,9 @@ pub async fn test_execute_order_shimless() { ***************** */ -/// Test that the execute order fallback instruction blocks the shimless instruction +/// Test that the execute order shim instruction blocks the shimless instruction #[tokio::test] -pub async fn test_execute_order_fallback_blocks_shimless() { +pub async fn test_execute_order_shim_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = VaaArgs { post_vaa: true, @@ -147,3 +222,101 @@ pub async fn test_execute_order_fallback_blocks_shimless() { .execute(&mut test_context, instruction_triggers, None) .await; } + +/// Test that execute order shim after close fast market order fails +#[tokio::test] +pub async fn test_execute_order_shim_after_close_fast_market_order_fails() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![ + InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig::default()), + ]; + let close_engines_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let expected_error = ExpectedError { + instruction_index: 0, + error_code: 3001, // Account Discriminator not found + error_string: "AccountDiscriminatorNotFound.".to_string(), + }; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( + ExecuteOrderInstructionConfig { + expected_error: Some(expected_error), + ..ExecuteOrderInstructionConfig::default() + }, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(close_engines_state), + ) + .await; +} + +/* +Helper code + */ + +pub enum ShimExecutionMode { + Shim, + Shimless, +} + +pub async fn execute_order_helper( + config: ExecuteOrderInstructionConfig, + shim_execution_mode: ShimExecutionMode, + vaa_args: Option, // If none, then defaults for shimexecutionmode are used + transfer_direction: TransferDirection, +) -> (TestingEngineState, ProgramTestContext, TestingEngine) { + let (place_initial_offer_state, mut test_context, testing_engine) = match shim_execution_mode { + ShimExecutionMode::Shim => { + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + vaa_args, + transfer_direction, + )) + .await + } + ShimExecutionMode::Shimless => { + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + vaa_args, + transfer_direction, + )) + .await + } + }; + let instruction_triggers = match shim_execution_mode { + ShimExecutionMode::Shim => vec![InstructionTrigger::ExecuteOrderShim(config)], + ShimExecutionMode::Shimless => vec![InstructionTrigger::ExecuteOrderShimless(config)], + }; + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification( + VerificationTrigger::VerifyAuctionState(true), + )); + ( + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) + .await, + test_context, + testing_engine, + ) +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index 78847258b..919085fab 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -38,15 +38,43 @@ use testing_engine::engine::{InstructionTrigger, TestingEngine}; use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; use utils::vaa::VaaArgs; +// Define a constant transfer direction for the tests +const TRANSFER_DIRECTION: TransferDirection = TransferDirection::FromEthereumToArbitrum; + /* - Happy path tests + Happy path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** * * ** +** ** ** ** + ** **** **** ** + ** ** ** ** + ** *** *** ** + *** **** **** *** + ** ****** ****** ** + *** *************** *** + **** **** + **** **** + ****** ****** + ***************** */ -/// Test that the place initial offer fallback instruction works correctly from arbitrum to ethereum +/// Test that the place initial offer shim instruction works correctly from arbitrum to ethereum #[tokio::test] -pub async fn test_place_initial_offer_fallback() { +pub async fn test_place_initial_offer_shim() { let config = PlaceInitialOfferInstructionConfig::default(); - let (final_state, _, _) = Box::pin(place_initial_offer_shim(config, None)).await; + let (final_state, _, _) = + Box::pin(place_initial_offer_shim(config, None, TRANSFER_DIRECTION)).await; assert_eq!( final_state .fast_market_order() @@ -68,12 +96,17 @@ pub async fn test_place_initial_offer_fallback() { #[tokio::test] pub async fn test_place_initial_offer_shimless() { let config = PlaceInitialOfferInstructionConfig::default(); - let (_final_state, _, _) = Box::pin(place_initial_offer_shimless(config, None)).await; + let (_final_state, _, _) = Box::pin(place_initial_offer_shimless( + config, + None, + TRANSFER_DIRECTION, + )) + .await; } /// Test that auction account is exactly the same when using shimless and fallback instructions #[tokio::test] -pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { +pub async fn test_place_initial_offer_shimless_and_fallback_auctions_are_identical() { let shimless_config = PlaceInitialOfferInstructionConfig { actor: TestingActorEnum::Owner, ..PlaceInitialOfferInstructionConfig::default() @@ -82,10 +115,16 @@ pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { actor: TestingActorEnum::Owner, ..PlaceInitialOfferInstructionConfig::default() }; - let (final_state_shimless, mut shimless_test_context, _) = - Box::pin(place_initial_offer_shimless(shimless_config, None)).await; - let (final_state_fallback, mut fallback_test_context, _) = - Box::pin(place_initial_offer_shim(fallback_config, None)).await; + let (final_state_shimless, mut shimless_test_context, _) = Box::pin( + place_initial_offer_shimless(shimless_config, None, TRANSFER_DIRECTION), + ) + .await; + let (final_state_fallback, mut fallback_test_context, _) = Box::pin(place_initial_offer_shim( + fallback_config, + None, + TRANSFER_DIRECTION, + )) + .await; let shimless_auction = { let shimless_active_auction_address = final_state_shimless @@ -124,8 +163,10 @@ pub async fn test_place_initial_offer_shimless_and_fallback_are_identical() { #[tokio::test] pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { let config = PlaceInitialOfferInstructionConfig::default(); - let (place_initial_offer_state, mut test_context, testing_engine) = - Box::pin(place_initial_offer_shimless(config, None)).await; + let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin( + place_initial_offer_shimless(config, None, TRANSFER_DIRECTION), + ) + .await; let improve_offer_config = ImproveOfferInstructionConfig::default(); let instruction_triggers = vec![InstructionTrigger::ImproveOfferShimless( improve_offer_config, @@ -169,7 +210,7 @@ pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { /// Test that the shimless place initial offer instruction blocks the shim instruction #[tokio::test] -pub async fn test_place_initial_offer_non_shim_blocks_shim() { +pub async fn test_place_initial_offer_shimless_blocks_shim() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = VaaArgs { post_vaa: true, @@ -211,7 +252,7 @@ pub async fn test_place_initial_offer_non_shim_blocks_shim() { /// Test that the place initial offer shim blocks the non shim instruction #[tokio::test] -pub async fn test_place_initial_offer_shim_blocks_non_shim() { +pub async fn test_place_initial_offer_shim_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = VaaArgs { post_vaa: true, @@ -254,7 +295,7 @@ pub async fn test_place_initial_offer_shim_blocks_non_shim() { /// Test with usdt token account #[tokio::test] -pub async fn test_place_initial_shim_offer_usdt_token_account() { +pub async fn test_place_initial_offer_shim_fails_usdt_token_account() { let expected_error = ExpectedError { instruction_index: 0, error_code: 3, // Token spl transfer error code when mint does not match @@ -265,12 +306,12 @@ pub async fn test_place_initial_shim_offer_usdt_token_account() { expected_error: Some(expected_error), ..PlaceInitialOfferInstructionConfig::default() }; - Box::pin(place_initial_offer_shim(config, None)).await; + Box::pin(place_initial_offer_shim(config, None, TRANSFER_DIRECTION)).await; } /// Test with usdt token account as custom account #[tokio::test] -pub async fn test_place_initial_shim_offer_usdt_mint_address() { +pub async fn test_place_initial_shim_offer_fails_usdt_mint_address() { let custom_accounts = PlaceInitialOfferCustomAccounts { mint_address: Some(crate::utils::constants::USDT_MINT), ..PlaceInitialOfferCustomAccounts::default() @@ -286,7 +327,7 @@ pub async fn test_place_initial_shim_offer_usdt_mint_address() { expected_error: Some(expected_error), ..PlaceInitialOfferInstructionConfig::default() }; - Box::pin(place_initial_offer_shim(config, None)).await; + Box::pin(place_initial_offer_shim(config, None, TRANSFER_DIRECTION)).await; } /// Test that the place initial offer fails if the fast market order is not created @@ -359,6 +400,7 @@ pub async fn test_place_initial_offer_shim_fails_when_offer_greater_than_max_fee Box::pin(place_initial_offer_shim( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -388,6 +430,7 @@ pub async fn test_place_initial_offer_shim_fails_when_amount_in_is_u64_max() { Box::pin(place_initial_offer_shim( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -418,6 +461,7 @@ pub async fn test_place_initial_offer_shim_fails_when_max_fee_and_amount_in_sum_ Box::pin(place_initial_offer_shim( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -439,7 +483,7 @@ pub async fn test_improve_offer_shim_fails_carping() { .create_vaa_args_and_initial_offer_config(); let (initial_offer_state, mut test_context, testing_engine) = Box::pin( - place_initial_offer_shim(initial_offer_config, Some(vaa_args)), + place_initial_offer_shim(initial_offer_config, Some(vaa_args), TRANSFER_DIRECTION), ) .await; @@ -484,7 +528,7 @@ pub async fn test_improve_offer_shim_fails_carping_second_improvement() { .create_vaa_args_and_initial_offer_config(); let (initial_offer_state, mut test_context, testing_engine) = Box::pin( - place_initial_offer_shim(initial_offer_config, Some(vaa_args)), + place_initial_offer_shim(initial_offer_config, Some(vaa_args), TRANSFER_DIRECTION), ) .await; let new_offer_price = amount_in.saturating_sub(1).saturating_div(2); @@ -548,6 +592,7 @@ pub async fn test_place_initial_offer_shim_when_offer_equals_max_fee() { Box::pin(place_initial_offer_shim( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -571,6 +616,7 @@ pub async fn test_place_initial_offer_shimless_when_offer_equals_max_fee() { Box::pin(place_initial_offer_shimless( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -603,6 +649,7 @@ pub async fn test_place_initial_offer_shim_when_deposit_amount_is_u256_max() { Box::pin(place_initial_offer_shim( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } @@ -635,16 +682,20 @@ pub async fn test_place_initial_offer_shimless_when_deposit_amount_is_u256_max() Box::pin(place_initial_offer_shimless( initial_offer_config, Some(vaa_args), + TRANSFER_DIRECTION, )) .await; } #[tokio::test] pub async fn test_improve_offer_after_close_fast_market_order() { - let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin( - place_initial_offer_shim(PlaceInitialOfferInstructionConfig::default(), None), - ) - .await; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + TRANSFER_DIRECTION, + )) + .await; let instruction_triggers = vec![ InstructionTrigger::CloseFastMarketOrderShim( CloseFastMarketOrderShimInstructionConfig::default(), @@ -660,6 +711,34 @@ pub async fn test_improve_offer_after_close_fast_market_order() { .await; } +#[tokio::test] +pub async fn test_improve_offer_after_reopen_fast_market_order() { + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + TRANSFER_DIRECTION, + )) + .await; + let reopen_fast_market_order_state = Box::pin(reopen_fast_market_order_shim( + place_initial_offer_state, + &mut test_context, + &testing_engine, + None, + )) + .await; + let improve_offer_trigger = vec![InstructionTrigger::ImproveOfferShimless( + ImproveOfferInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + improve_offer_trigger, + Some(reopen_fast_market_order_state), + ) + .await; +} + /* ================================================================================ Helper structs and functions @@ -669,8 +748,8 @@ Helper structs and functions pub async fn place_initial_offer_shim( config: PlaceInitialOfferInstructionConfig, vaa_args: Option, + transfer_direction: TransferDirection, ) -> (TestingEngineState, ProgramTestContext, TestingEngine) { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { post_vaa: false, ..VaaArgs::default() @@ -707,8 +786,8 @@ pub async fn place_initial_offer_shim( pub async fn place_initial_offer_shimless( config: PlaceInitialOfferInstructionConfig, vaa_args: Option, + transfer_direction: TransferDirection, ) -> (TestingEngineState, ProgramTestContext, TestingEngine) { - let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { post_vaa: true, ..VaaArgs::default() @@ -736,6 +815,43 @@ pub async fn place_initial_offer_shimless( ) } +pub async fn reopen_fast_market_order_shim( + initial_state: TestingEngineState, + test_context: &mut ProgramTestContext, + testing_engine: &TestingEngine, + configs: Option<( + InitializeFastMarketOrderShimInstructionConfig, + CloseFastMarketOrderShimInstructionConfig, + )>, +) -> TestingEngineState { + // If no configs are provided, assume its the first reopening + let (reopen_config, close_config) = configs.unwrap_or_else(|| { + let correct_solver = &testing_engine + .testing_context + .testing_actors + .solvers + .get(1) + .unwrap() + .pubkey(); + ( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 1, + close_account_refund_recipient: Some(*correct_solver), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + CloseFastMarketOrderShimInstructionConfig::default(), + ) + }); + let instruction_triggers = vec![ + InstructionTrigger::CloseFastMarketOrderShim(close_config), + InstructionTrigger::InitializeFastMarketOrderShim(reopen_config), + ]; + + testing_engine + .execute(test_context, instruction_triggers, Some(initial_state)) + .await +} + /// A struct representing the auction info and its valid state // TODO: Use this or something similar to fuzz test over various initial offers. #[derive(Clone)] diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index cc4e7055a..129c46faf 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -18,6 +18,8 @@ //! - `test_prepare_order_response_shimful_blocks_shimless` - Test that the prepare order response shimful instruction blocks the shimless instruction //! +use crate::test_scenarios::execute_order::{execute_order_helper, ShimExecutionMode}; +use crate::test_scenarios::make_offer::place_initial_offer_shim; use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, PrepareOrderInstructionConfig, @@ -31,9 +33,39 @@ use testing_engine::engine::{InstructionTrigger, TestingEngine}; use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; use utils::vaa::VaaArgs; -/// Test that the prepare order fallback instruction works correctly (from ethereum to arbitrum) +use super::make_offer::reopen_fast_market_order_shim; + +/* + Happy path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** * * ** +** ** ** ** + ** **** **** ** + ** ** ** ** + ** *** *** ** + *** **** **** *** + ** ****** ****** ** + *** *************** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + +/// Test that the prepare order shim instruction works correctly (from ethereum to arbitrum) #[tokio::test] -pub async fn test_prepare_order_shim_fallback() { +pub async fn test_prepare_order_shim() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; let vaa_args = VaaArgs { post_vaa: false, @@ -95,8 +127,187 @@ pub async fn test_prepare_order_shimless() { .await; } +/// Test that prepare order response shim works after executing order shimlessly +#[tokio::test] +pub async fn test_prepare_order_response_shim_after_execute_order_shimless() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (execute_order_state, mut test_context, testing_engine) = Box::pin(execute_order_helper( + ExecuteOrderInstructionConfig::default(), + ShimExecutionMode::Shimless, + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(execute_order_state), + ) + .await; +} + +/// Test that prepare order response shimless works after executing order shimlessly +#[tokio::test] +pub async fn test_prepare_order_response_shimless_after_execute_order_shim() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (execute_order_state, mut test_context, testing_engine) = Box::pin(execute_order_helper( + ExecuteOrderInstructionConfig::default(), + ShimExecutionMode::Shim, + Some(VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }), + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::PrepareOrderShimless( + PrepareOrderInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(execute_order_state), + ) + .await; +} + +/// Test prepare order response shim after reopening fast market order account in between offer and execute order +#[tokio::test] +pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_account() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let reopen_fast_market_order_state = Box::pin(reopen_fast_market_order_shim( + place_initial_offer_state, + &mut test_context, + &testing_engine, + None, + )) + .await; + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(reopen_fast_market_order_state), + ) + .await; +} + +/// Test that prepare order response shim works after reopening fast market order after place initial offer AND execute order +#[tokio::test] +pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_account_after_execute_order_and_place_initial_offer( +) { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let reopen_fast_market_order_state = reopen_fast_market_order_shim( + place_initial_offer_state, + &mut test_context, + &testing_engine, + None, + ) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( + ExecuteOrderInstructionConfig::default(), + )]; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(reopen_fast_market_order_state), + ) + .await; + let second_solver_keypair = testing_engine + .testing_context + .testing_actors + .solvers + .get(1) + .unwrap() + .clone() + .keypair(); + let third_solver_pubkey = &testing_engine + .testing_context + .testing_actors + .solvers + .get(2) + .unwrap() + .pubkey(); + let reopen_config = InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 2, + close_account_refund_recipient: Some(*third_solver_pubkey), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }; + let close_config = CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some(second_solver_keypair), + ..CloseFastMarketOrderShimInstructionConfig::default() + }; + let double_reopen_fast_market_order_state = reopen_fast_market_order_shim( + execute_order_state, + &mut test_context, + &testing_engine, + Some((reopen_config, close_config)), + ) + .await; + let instruction_triggers = vec![InstructionTrigger::PrepareOrderShim( + PrepareOrderInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(double_reopen_fast_market_order_state), + ) + .await; +} + /* - Sad path tests + Sad path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** ** +** ** +** ** + ** ************ ** + ** ****** ****** ** + *** ***** ***** *** + ** *** *** ** + *** ** ** *** + **** **** + **** **** + ****** ****** + ***************** */ /// Test that the prepare order response shimless instruction blocks the shimful instruction diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index a19bdeb3d..5fe5223ed 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -22,6 +22,8 @@ //! testing_engine.execute(instruction_triggers).await; //! ``` +use std::ops::{Deref, DerefMut}; + use matching_engine::state::FastMarketOrder; use solana_program_test::ProgramTestContext; use solana_sdk::signer::Signer; @@ -48,7 +50,6 @@ use crate::utils::{ }; use anchor_lang::prelude::*; -#[allow(dead_code)] pub enum InstructionTrigger { InitializeProgram(InitializeInstructionConfig), CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig), @@ -64,6 +65,60 @@ pub enum InstructionTrigger { CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } +pub enum VerificationTrigger { + // Verify that the auction state is as expected (bool is expected to succeed) + VerifyAuctionState(bool), +} + +pub enum ExecutionTrigger { + Instruction(InstructionTrigger), + Verification(VerificationTrigger), +} + +impl From for ExecutionTrigger { + fn from(trigger: InstructionTrigger) -> Self { + ExecutionTrigger::Instruction(trigger) + } +} + +impl From for ExecutionTrigger { + fn from(trigger: VerificationTrigger) -> Self { + ExecutionTrigger::Verification(trigger) + } +} + +pub struct ExecutionChain(Vec); + +impl Deref for ExecutionChain { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ExecutionChain { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl ExecutionChain { + pub fn instruction_triggers(&self) -> Vec<&InstructionTrigger> { + self.iter() + .filter_map(|trigger| match trigger { + ExecutionTrigger::Instruction(trigger) => Some(trigger), + _ => None, + }) + .collect() + } +} +impl From> for ExecutionChain { + fn from(triggers: Vec) -> Self { + Self(triggers.into_iter().map(|trigger| trigger.into()).collect()) + } +} + impl InstructionTrigger { pub fn is_shim(&self) -> bool { matches!( @@ -174,25 +229,29 @@ impl TestingEngine { pub async fn execute( &self, test_context: &mut ProgramTestContext, - instruction_chain: Vec, + execution_chain: impl Into, initial_state: Option, ) -> TestingEngineState { let mut current_state = initial_state.unwrap_or_else(|| self.create_initial_state()); + let execution_chain = execution_chain.into(); + self.verify_triggers(&execution_chain); - self.verify_triggers(&instruction_chain); - - for trigger in instruction_chain { + for trigger in execution_chain.iter() { current_state = self - .execute_trigger(test_context, ¤t_state, &trigger) + .execute_trigger(test_context, ¤t_state, trigger) .await; } current_state } /// Verifies that the shimmode corresponds to the instruction chain - fn verify_triggers(&self, instruction_chain: &[InstructionTrigger]) { + fn verify_triggers(&self, execution_chain: &ExecutionChain) { // If any shim instructions are present, make sure that shim mode is set to VerifyAndPostSignature - if instruction_chain.iter().any(|trigger| trigger.is_shim()) { + if execution_chain + .instruction_triggers() + .iter() + .any(|trigger| trigger.is_shim()) + { assert_eq!( self.testing_context.shim_mode, ShimMode::VerifyAndPostSignature, @@ -212,57 +271,65 @@ impl TestingEngine { &self, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, - trigger: &InstructionTrigger, + trigger: &ExecutionTrigger, ) -> TestingEngineState { match trigger { - InstructionTrigger::InitializeProgram(config) => { - self.initialize_program(test_context, current_state, config) - .await - } - InstructionTrigger::CreateCctpRouterEndpoints(config) => { - self.create_cctp_router_endpoints(test_context, current_state, config) - .await - } - InstructionTrigger::InitializeFastMarketOrderShim(config) => { - self.create_fast_market_order_account(test_context, current_state, config) - .await - } - InstructionTrigger::CloseFastMarketOrderShim(config) => { - self.close_fast_market_order_account(test_context, current_state, config) - .await - } - InstructionTrigger::PlaceInitialOfferShimless(config) => { - self.place_initial_offer_shimless(test_context, current_state, config) - .await - } - InstructionTrigger::PlaceInitialOfferShim(config) => { - self.place_initial_offer_shim(test_context, current_state, config) - .await - } - InstructionTrigger::ImproveOfferShimless(config) => { - self.improve_offer_shimless(test_context, current_state, config) - .await - } - InstructionTrigger::ExecuteOrderShim(config) => { - self.execute_order_shim(test_context, current_state, config) - .await - } - InstructionTrigger::ExecuteOrderShimless(config) => { - self.execute_order_shimless(test_context, current_state, config) - .await - } - InstructionTrigger::PrepareOrderShim(config) => { - self.prepare_order_shim(test_context, current_state, config) - .await - } - InstructionTrigger::PrepareOrderShimless(config) => { - self.prepare_order_shimless(test_context, current_state, config) - .await - } - InstructionTrigger::SettleAuction(config) => { - self.settle_auction(test_context, current_state, config) - .await - } + ExecutionTrigger::Instruction(trigger) => match trigger { + InstructionTrigger::InitializeProgram(config) => { + self.initialize_program(test_context, current_state, config) + .await + } + InstructionTrigger::CreateCctpRouterEndpoints(config) => { + self.create_cctp_router_endpoints(test_context, current_state, config) + .await + } + InstructionTrigger::InitializeFastMarketOrderShim(config) => { + self.create_fast_market_order_account(test_context, current_state, config) + .await + } + InstructionTrigger::CloseFastMarketOrderShim(config) => { + self.close_fast_market_order_account(test_context, current_state, config) + .await + } + InstructionTrigger::PlaceInitialOfferShimless(config) => { + self.place_initial_offer_shimless(test_context, current_state, config) + .await + } + InstructionTrigger::PlaceInitialOfferShim(config) => { + self.place_initial_offer_shim(test_context, current_state, config) + .await + } + InstructionTrigger::ImproveOfferShimless(config) => { + self.improve_offer_shimless(test_context, current_state, config) + .await + } + InstructionTrigger::ExecuteOrderShim(config) => { + self.execute_order_shim(test_context, current_state, config) + .await + } + InstructionTrigger::ExecuteOrderShimless(config) => { + self.execute_order_shimless(test_context, current_state, config) + .await + } + InstructionTrigger::PrepareOrderShim(config) => { + self.prepare_order_shim(test_context, current_state, config) + .await + } + InstructionTrigger::PrepareOrderShimless(config) => { + self.prepare_order_shimless(test_context, current_state, config) + .await + } + InstructionTrigger::SettleAuction(config) => { + self.settle_auction(test_context, current_state, config) + .await + } + }, + ExecutionTrigger::Verification(trigger) => match trigger { + VerificationTrigger::VerifyAuctionState(expected_to_succeed) => { + self.verify_auction_state(test_context, current_state, *expected_to_succeed) + .await + } + }, } } @@ -439,6 +506,8 @@ impl TestingEngine { guardian_set_address: guardian_signature_info.guardian_set_pubkey, guardian_signatures_address: guardian_signature_info.guardian_signatures_pubkey, }, + auction_state: current_state.auction_state().clone(), + auction_accounts: current_state.auction_accounts().cloned(), } } else { current_state.clone() @@ -852,6 +921,24 @@ impl TestingEngine { _ => current_state.clone(), } } + + async fn verify_auction_state( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + expected_to_succeed: bool, + ) -> TestingEngineState { + let auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction state expected"); + let was_success = auction_state + .verify_auction(&self.testing_context, test_context) + .await + .is_ok(); + assert_eq!(was_success, expected_to_succeed); + current_state.clone() + } } /// Fast forwards the slot in the test context diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 26645f262..a74218550 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -101,6 +101,8 @@ pub enum TestingEngineState { router_endpoints: Option, fast_market_order: FastMarketOrderAccountCreatedState, guardian_set_state: GuardianSetState, + auction_state: AuctionState, + auction_accounts: Option, }, InitialOfferPlaced { base: BaseState, @@ -258,6 +260,7 @@ impl TestingEngineState { Self::OrderPrepared { auction_state, .. } => auction_state, Self::AuctionSettled { auction_state, .. } => auction_state, Self::FastMarketOrderClosed { auction_state, .. } => auction_state, + Self::FastMarketOrderAccountCreated { auction_state, .. } => auction_state, _ => &AuctionState::Inactive, } } @@ -282,6 +285,9 @@ impl TestingEngineState { Self::FastMarketOrderClosed { auction_accounts, .. } => auction_accounts.as_ref(), + Self::FastMarketOrderAccountCreated { + auction_accounts, .. + } => auction_accounts.as_ref(), _ => None, } } diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 35127c674..385b7168a 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -4,7 +4,7 @@ use solana_program_test::ProgramTestContext; use super::Chain; use super::{router::TestRouterEndpoints, token_account::SplTokenEnum}; use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; -use anyhow::{anyhow, Result as AnyhowResult}; +use anyhow::{anyhow, ensure, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionInfo}; /// A struct representing the accounts for an auction @@ -179,18 +179,39 @@ impl ActiveAuctionState { redeemer_message_len: 0, // Not tested against destination_asset_info: None, // Not tested against }; - assert_eq!(auction_info.config_id, expected_auction_info.config_id); + ensure!( + auction_info.config_id == expected_auction_info.config_id, + "Auction config_id mismatch: expected {:?}, got {:?}", + expected_auction_info.config_id, + auction_info.config_id + ); - assert_eq!(auction_info.start_slot, expected_auction_info.start_slot); + ensure!( + auction_info.start_slot == expected_auction_info.start_slot, + "Auction start_slot mismatch: expected {}, got {}", + expected_auction_info.start_slot, + auction_info.start_slot + ); - assert_eq!(auction_info.offer_price, expected_auction_info.offer_price); - assert_eq!( - auction_info.best_offer_token, - expected_auction_info.best_offer_token + ensure!( + auction_info.offer_price == expected_auction_info.offer_price, + "Auction offer_price mismatch: expected {}, got {}", + expected_auction_info.offer_price, + auction_info.offer_price ); - assert_eq!( - auction_info.initial_offer_token, - expected_auction_info.initial_offer_token + + ensure!( + auction_info.best_offer_token == expected_auction_info.best_offer_token, + "Auction best_offer_token mismatch: expected {:?}, got {:?}", + expected_auction_info.best_offer_token, + auction_info.best_offer_token + ); + + ensure!( + auction_info.initial_offer_token == expected_auction_info.initial_offer_token, + "Auction initial_offer_token mismatch: expected {:?}, got {:?}", + expected_auction_info.initial_offer_token, + auction_info.initial_offer_token ); Ok(()) } From 93c6ccbec69c89a037fb93dcf4f3d967bc7bfb7a Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 8 Apr 2025 15:54:16 +0100 Subject: [PATCH 057/112] idl built --- .../tests/test_scenarios/execute_order.rs | 27 ++++---- .../tests/test_scenarios/prepare_order.rs | 8 +-- .../tests/test_scenarios/settle_auction.rs | 61 +++++++++++++++++++ .../tests/testing_engine/engine.rs | 47 +++++++------- solana/ts/src/idl/json/matching_engine.json | 6 +- solana/ts/src/idl/ts/matching_engine.ts | 6 +- 6 files changed, 112 insertions(+), 43 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 893c6dcfb..6479781a6 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -31,12 +31,12 @@ use super::make_offer::place_initial_offer_shim; // TODO: Flesh out this test to see if the message was posted correctly pub async fn test_execute_order_shim() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - execute_order_helper( + Box::pin(execute_order_helper( ExecuteOrderInstructionConfig::default(), ShimExecutionMode::Shim, None, transfer_direction, - ) + )) .await; } @@ -44,12 +44,12 @@ pub async fn test_execute_order_shim() { #[tokio::test] pub async fn test_execute_order_shimless() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - execute_order_helper( + Box::pin(execute_order_helper( ExecuteOrderInstructionConfig::default(), ShimExecutionMode::Shimless, None, transfer_direction, - ) + )) .await; } @@ -75,18 +75,15 @@ pub async fn test_execute_order_after_reopening_fast_market_order_account() { InitializeFastMarketOrderShimInstructionConfig { fast_market_order_id: 1, close_account_refund_recipient: Some(close_account_refund_recipient), - payer_signer: None, - expected_error: None, - expected_log_messages: None, ..InitializeFastMarketOrderShimInstructionConfig::default() }, ), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; let mut execution_chain = ExecutionChain::from(instruction_triggers); - execution_chain.push(ExecutionTrigger::Verification( + execution_chain.push(ExecutionTrigger::Verification(Box::new( VerificationTrigger::VerifyAuctionState(true), - )); + ))); let _ = testing_engine .execute( &mut test_context, @@ -114,9 +111,9 @@ pub async fn test_execute_order_shim_after_placing_initial_offer_with_shimless() InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), ]; let mut execution_chain = ExecutionChain::from(instruction_triggers); - execution_chain.push(ExecutionTrigger::Verification( + execution_chain.push(ExecutionTrigger::Verification(Box::new( VerificationTrigger::VerifyAuctionState(true), - )); + ))); let _ = testing_engine .execute( &mut test_context, @@ -144,9 +141,9 @@ pub async fn test_execute_order_shimless_after_placing_initial_offer_with_shim() ExecuteOrderInstructionConfig::default(), )]; let mut execution_chain = ExecutionChain::from(instruction_triggers); - execution_chain.push(ExecutionTrigger::Verification( + execution_chain.push(ExecutionTrigger::Verification(Box::new( VerificationTrigger::VerifyAuctionState(true), - )); + ))); let _ = testing_engine .execute( &mut test_context, @@ -305,9 +302,9 @@ pub async fn execute_order_helper( ShimExecutionMode::Shimless => vec![InstructionTrigger::ExecuteOrderShimless(config)], }; let mut execution_chain = ExecutionChain::from(instruction_triggers); - execution_chain.push(ExecutionTrigger::Verification( + execution_chain.push(ExecutionTrigger::Verification(Box::new( VerificationTrigger::VerifyAuctionState(true), - )); + ))); ( testing_engine .execute( diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index 129c46faf..d1d4a404d 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -222,12 +222,12 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ transfer_direction, )) .await; - let reopen_fast_market_order_state = reopen_fast_market_order_shim( + let reopen_fast_market_order_state = Box::pin(reopen_fast_market_order_shim( place_initial_offer_state, &mut test_context, &testing_engine, None, - ) + )) .await; let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( ExecuteOrderInstructionConfig::default(), @@ -263,12 +263,12 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ close_account_refund_recipient_keypair: Some(second_solver_keypair), ..CloseFastMarketOrderShimInstructionConfig::default() }; - let double_reopen_fast_market_order_state = reopen_fast_market_order_shim( + let double_reopen_fast_market_order_state = Box::pin(reopen_fast_market_order_shim( execute_order_state, &mut test_context, &testing_engine, Some((reopen_config, close_config)), - ) + )) .await; let instruction_triggers = vec![InstructionTrigger::PrepareOrderShim( PrepareOrderInstructionConfig::default(), diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index 3fa99e568..c9bf8314e 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -8,6 +8,7 @@ //! //! - `test_settle_auction_complete` - Test that the settle auction instruction works correctly //! +use crate::test_scenarios::make_offer::{place_initial_offer_shim, reopen_fast_market_order_shim}; use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, @@ -20,6 +21,34 @@ use testing_engine::engine::{InstructionTrigger, TestingEngine}; use testing_engine::setup::{setup_environment, ShimMode, TransferDirection}; use utils::vaa::VaaArgs; +/* + Happy path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** * * ** +** ** ** ** + ** **** **** ** + ** ** ** ** + ** *** *** ** + *** **** **** *** + ** ****** ****** ** + *** *************** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + /// Test that the settle auction instruction works correctly #[tokio::test] pub async fn test_settle_auction_complete() { @@ -53,3 +82,35 @@ pub async fn test_settle_auction_complete() { .execute(&mut test_context, instruction_triggers, None) .await; } + +/// Test that the settle auction instruction works with reopened fast market order +#[tokio::test] +pub async fn test_settle_auction_reopened_fast_market_order() { + let (initial_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + Some(VaaArgs::default()), + TransferDirection::FromEthereumToArbitrum, + )) + .await; + + let reopen_fast_market_order_state = Box::pin(reopen_fast_market_order_shim( + initial_state, + &mut test_context, + &testing_engine, + None, + )) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(reopen_fast_market_order_state), + ) + .await; +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 5fe5223ed..f88081169 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -71,19 +71,19 @@ pub enum VerificationTrigger { } pub enum ExecutionTrigger { - Instruction(InstructionTrigger), - Verification(VerificationTrigger), + Instruction(Box), + Verification(Box), } impl From for ExecutionTrigger { fn from(trigger: InstructionTrigger) -> Self { - ExecutionTrigger::Instruction(trigger) + ExecutionTrigger::Instruction(Box::new(trigger)) } } impl From for ExecutionTrigger { fn from(trigger: VerificationTrigger) -> Self { - ExecutionTrigger::Verification(trigger) + ExecutionTrigger::Verification(Box::new(trigger)) } } @@ -106,9 +106,12 @@ impl DerefMut for ExecutionChain { impl ExecutionChain { pub fn instruction_triggers(&self) -> Vec<&InstructionTrigger> { self.iter() - .filter_map(|trigger| match trigger { - ExecutionTrigger::Instruction(trigger) => Some(trigger), - _ => None, + .filter_map(|trigger| { + if let ExecutionTrigger::Instruction(boxed_trigger) = trigger { + Some(boxed_trigger.as_ref()) + } else { + None + } }) .collect() } @@ -274,59 +277,59 @@ impl TestingEngine { trigger: &ExecutionTrigger, ) -> TestingEngineState { match trigger { - ExecutionTrigger::Instruction(trigger) => match trigger { - InstructionTrigger::InitializeProgram(config) => { + ExecutionTrigger::Instruction(trigger) => match **trigger { + InstructionTrigger::InitializeProgram(ref config) => { self.initialize_program(test_context, current_state, config) .await } - InstructionTrigger::CreateCctpRouterEndpoints(config) => { + InstructionTrigger::CreateCctpRouterEndpoints(ref config) => { self.create_cctp_router_endpoints(test_context, current_state, config) .await } - InstructionTrigger::InitializeFastMarketOrderShim(config) => { + InstructionTrigger::InitializeFastMarketOrderShim(ref config) => { self.create_fast_market_order_account(test_context, current_state, config) .await } - InstructionTrigger::CloseFastMarketOrderShim(config) => { + InstructionTrigger::CloseFastMarketOrderShim(ref config) => { self.close_fast_market_order_account(test_context, current_state, config) .await } - InstructionTrigger::PlaceInitialOfferShimless(config) => { + InstructionTrigger::PlaceInitialOfferShimless(ref config) => { self.place_initial_offer_shimless(test_context, current_state, config) .await } - InstructionTrigger::PlaceInitialOfferShim(config) => { + InstructionTrigger::PlaceInitialOfferShim(ref config) => { self.place_initial_offer_shim(test_context, current_state, config) .await } - InstructionTrigger::ImproveOfferShimless(config) => { + InstructionTrigger::ImproveOfferShimless(ref config) => { self.improve_offer_shimless(test_context, current_state, config) .await } - InstructionTrigger::ExecuteOrderShim(config) => { + InstructionTrigger::ExecuteOrderShim(ref config) => { self.execute_order_shim(test_context, current_state, config) .await } - InstructionTrigger::ExecuteOrderShimless(config) => { + InstructionTrigger::ExecuteOrderShimless(ref config) => { self.execute_order_shimless(test_context, current_state, config) .await } - InstructionTrigger::PrepareOrderShim(config) => { + InstructionTrigger::PrepareOrderShim(ref config) => { self.prepare_order_shim(test_context, current_state, config) .await } - InstructionTrigger::PrepareOrderShimless(config) => { + InstructionTrigger::PrepareOrderShimless(ref config) => { self.prepare_order_shimless(test_context, current_state, config) .await } - InstructionTrigger::SettleAuction(config) => { + InstructionTrigger::SettleAuction(ref config) => { self.settle_auction(test_context, current_state, config) .await } }, - ExecutionTrigger::Verification(trigger) => match trigger { + ExecutionTrigger::Verification(trigger) => match **trigger { VerificationTrigger::VerifyAuctionState(expected_to_succeed) => { - self.verify_auction_state(test_context, current_state, *expected_to_succeed) + self.verify_auction_state(test_context, current_state, expected_to_succeed) .await } }, diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index 8d37b256e..d7eb17f4a 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -3177,7 +3177,11 @@ "msg": "From and to router endpoints are the same" }, { - "code": 8050, + "code": 8576, + "name": "MismatchingCloseAccountRefundRecipient" + }, + { + "code": 8306, "name": "InvalidCctpMessage" } ], diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 2466c267d..5fcd92ae5 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -3183,7 +3183,11 @@ export type MatchingEngine = { "msg": "From and to router endpoints are the same" }, { - "code": 8050, + "code": 8576, + "name": "mismatchingCloseAccountRefundRecipient" + }, + { + "code": 8306, "name": "invalidCctpMessage" } ], From 7c3cbfa2ff132c4e4ca885e4720002ecbd90d882 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Mon, 14 Apr 2025 18:46:50 +0100 Subject: [PATCH 058/112] more negative tests added --- .../matching-engine-testing/Cargo.toml | 35 +- .../tests/shimful/shims_execute_order.rs | 12 +- .../tests/shimful/shims_make_offer.rs | 5 +- .../shimful/shims_prepare_order_response.rs | 2 + .../tests/shimless/initialize.rs | 10 +- .../tests/shimless/make_offer.rs | 5 +- .../tests/shimless/mod.rs | 1 + .../tests/shimless/pause_custodian.rs | 71 ++++ .../tests/shimless/prepare_order_response.rs | 2 + .../tests/shimless/settle_auction.rs | 23 +- .../tests/test_scenarios/execute_order.rs | 249 ++++++++++++- .../test_scenarios/initialise_and_misc.rs | 5 +- .../tests/test_scenarios/make_offer.rs | 112 ++++++ .../tests/test_scenarios/settle_auction.rs | 213 +++++++++++ .../tests/testing_engine/config.rs | 228 +++++++++++- .../tests/testing_engine/engine.rs | 154 ++++++-- .../tests/testing_engine/setup.rs | 155 +++++++- .../tests/testing_engine/state.rs | 180 ++++++++- .../tests/utils/auction.rs | 345 +++++++++++++++++- .../tests/utils/token_account.rs | 30 +- .../tests/utils/vaa.rs | 34 +- .../processor/close_fast_market_order.rs | 17 +- .../src/fallback/processor/execute_order.rs | 9 +- .../fallback/processor/place_initial_offer.rs | 2 +- 24 files changed, 1799 insertions(+), 100 deletions(-) create mode 100644 solana/modules/matching-engine-testing/tests/shimless/pause_custodian.rs diff --git a/solana/modules/matching-engine-testing/Cargo.toml b/solana/modules/matching-engine-testing/Cargo.toml index 62fb2edf8..17a9eb9f8 100644 --- a/solana/modules/matching-engine-testing/Cargo.toml +++ b/solana/modules/matching-engine-testing/Cargo.toml @@ -61,6 +61,37 @@ tracing-log = "0.2.0" once_cell = "1.8" anyhow = "1.0.97" -[lints] -workspace = true +[lints.clippy] +correctness = { priority = -1, level = "warn"} +inconsistent_digit_grouping = "allow" + +### See clippy.toml. +unnecessary_lazy_evaluations = "allow" +or_fun_call = "warn" + +arithmetic_side_effects = "allow" +as_conversions = "allow" +cast_abs_to_unsigned = "allow" +cast_lossless= "allow" +cast_possible_truncation = "allow" +cast_possible_wrap = "allow" +cast_precision_loss = "deny" +cast_sign_loss = "deny" +eq_op = "deny" +expect_used = "deny" +float_cmp = "deny" +integer_division = "allow" +large_futures = "deny" +large_stack_arrays = "deny" +large_stack_frames = "deny" +lossy_float_literal = "deny" +manual_slice_size_calculation = "deny" +modulo_one = "deny" +out_of_bounds_indexing = "deny" +overflow_check_conditional = "deny" +panic = "allow" +recursive_format_impl = "deny" +todo = "allow" +unchecked_duration_subtraction = "allow" +unreachable = "deny" diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index be1f81da4..6d99b7961 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -3,7 +3,6 @@ use crate::testing_engine::config::{ }; use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::testing_engine::state::TestingEngineState; -use crate::utils::token_account::SplTokenEnum; use super::super::utils; use anchor_spl::token::spl_token; @@ -107,7 +106,7 @@ pub async fn execute_order_fallback( let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let execute_order_fallback_fixture = create_execute_order_fallback_fixture( testing_context, @@ -190,11 +189,8 @@ pub fn create_execute_order_fallback_fixture( &POST_MESSAGE_SHIM_PROGRAM_ID, ) .0; - let solver = testing_context.testing_actors.solvers[config.solver_index].clone(); - let executor_token = solver - .actor - .token_account_address(&SplTokenEnum::Usdc) - .unwrap(); + let solver = config.actor_enum.get_actor(&testing_context.testing_actors); + let executor_token = solver.token_account_address(&config.token_enum).unwrap(); ExecuteOrderFallbackFixture { cctp_message, post_message_sequence, @@ -279,7 +275,7 @@ pub async fn execute_order_fallback_test( let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new(current_state, &payer_signer.pubkey(), &fixture_accounts); execute_order_fallback( diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 520548124..5c091a3c5 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -46,7 +46,7 @@ pub async fn place_initial_offer_fallback( let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let close_account_refund_recipient = current_state .fast_market_order() .unwrap() @@ -73,6 +73,7 @@ pub async fn place_initial_offer_fallback( let fast_market_order = create_fast_market_order_state_from_vaa_data(vaa_data, close_account_refund_recipient); let offer_price = config.offer_price; + let actor_enum = config.actor; let offer_actor = config.actor.get_actor(&testing_context.testing_actors); let offer_token = match &config.custom_accounts { Some(custom_accounts) => match custom_accounts.offer_token_address { @@ -186,11 +187,13 @@ pub async fn place_initial_offer_fallback( auction_custody_token_address, auction_config_address, initial_offer: utils::auction::AuctionOffer { + actor: actor_enum, participant: payer_signer.pubkey(), offer_token, offer_price, }, best_offer: utils::auction::AuctionOffer { + actor: actor_enum, participant: payer_signer.pubkey(), offer_token, offer_price, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index cc872907f..46660288d 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -265,6 +265,7 @@ pub async fn prepare_order_response_cctp_shim( Some(PrepareOrderResponseShimFixture { prepared_order_response: prepared_order_response_pda, prepared_custody_token: prepared_custody_token_pda, + base_fee_token: accounts.base_fee_token, }) } else { None @@ -383,4 +384,5 @@ pub async fn prepare_order_response_test( pub struct PrepareOrderResponseShimFixture { pub prepared_order_response: Pubkey, pub prepared_custody_token: Pubkey, + pub base_fee_token: Pubkey, } diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 93bc25fab..4518f805b 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -2,7 +2,7 @@ use solana_program_test::ProgramTestContext; use solana_sdk::{ instruction::Instruction, pubkey::Pubkey, - signature::Signer, + signature::{Keypair, Signer}, transaction::{Transaction, VersionedTransaction}, }; @@ -144,6 +144,7 @@ pub async fn initialize_program( testing_context: &TestingContext, test_context: &mut ProgramTestContext, auction_parameters_config: AuctionParametersConfig, + payer_signer: &Keypair, expected_error: Option<&ExpectedError>, expected_log_messages: Option<&Vec>, ) -> Option { @@ -201,16 +202,15 @@ pub async fn initialize_program( data: ix_data.data(), }; // Create and sign transaction - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&test_context.payer.pubkey())); + let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer_signer.pubkey())); let new_blockhash = testing_context .get_new_latest_blockhash(test_context) .await .expect("Could not get new blockhash"); transaction.sign( &[ - &test_context.payer, - &testing_context.testing_actors.owner.keypair(), + &payer_signer, + &testing_context.testing_actors.owner.keypair().as_ref(), ], new_blockhash, ); diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index ced3459fa..a3a8ce28a 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -57,7 +57,7 @@ pub async fn place_initial_offer_shimless( let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let offer_actor = config.actor.get_actor(&testing_context.testing_actors); let offer_token = offer_actor .token_account_address(&config.spl_token_enum) @@ -253,11 +253,13 @@ pub async fn place_initial_offer_shimless( auction_custody_token_address, auction_config_address, initial_offer: AuctionOffer { + actor: config.actor, participant: payer_signer.pubkey(), offer_token, offer_price: initial_offer_ix.offer_price, }, best_offer: AuctionOffer { + actor: config.actor, participant: payer_signer.pubkey(), offer_token, offer_price: initial_offer_ix.offer_price, @@ -397,6 +399,7 @@ pub async fn improve_offer( auction_config_address: auction_config, initial_offer: initial_offer.clone(), best_offer: AuctionOffer { + actor: config.actor, participant: payer_signer.pubkey(), offer_token, offer_price, diff --git a/solana/modules/matching-engine-testing/tests/shimless/mod.rs b/solana/modules/matching-engine-testing/tests/shimless/mod.rs index eb45b45ee..62fc2076a 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/mod.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/mod.rs @@ -3,5 +3,6 @@ pub mod execute_order; pub mod initialize; pub mod make_offer; +pub mod pause_custodian; pub mod prepare_order_response; pub mod settle_auction; diff --git a/solana/modules/matching-engine-testing/tests/shimless/pause_custodian.rs b/solana/modules/matching-engine-testing/tests/shimless/pause_custodian.rs new file mode 100644 index 000000000..d3646cfe9 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/shimless/pause_custodian.rs @@ -0,0 +1,71 @@ +use std::rc::Rc; + +use crate::testing_engine::config::ExpectedError; + +use crate::testing_engine::state::TestingEngineState; + +use anchor_lang::prelude::*; +use anchor_lang::InstructionData; +use matching_engine::accounts::AdminMut; + +use crate::testing_engine::setup::TestingContext; + +use matching_engine::accounts::SetPause as SetPauseAccounts; +use matching_engine::instruction::SetPause as SetPauseIx; +use solana_program_test::ProgramTestContext; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::Keypair; +use solana_sdk::signature::Signer; +use solana_sdk::transaction::Transaction; + +/// Pause the custodian +/// +/// # Arguments +/// +/// * `test_context` - The test context +/// * `current_state` - The current state +/// * `config` - The config +/// +/// # Returns +/// +/// The new paused state +pub async fn set_pause( + test_context: &mut ProgramTestContext, + testing_context: &TestingContext, + current_state: &TestingEngineState, + owner_or_assistant: &Rc, + expected_error: Option<&ExpectedError>, + is_paused: bool, +) -> TestingEngineState { + let custodian_address = current_state.initialized().unwrap().custodian_address; + let admin_mut = AdminMut { + owner_or_assistant: owner_or_assistant.pubkey(), + custodian: custodian_address, + }; + let accounts = SetPauseAccounts { admin: admin_mut }; + let instruction_data = SetPauseIx { pause: is_paused }.data(); + let instruction = Instruction { + program_id: testing_context.get_matching_engine_program_id(), + accounts: accounts.to_account_metas(None), + data: instruction_data, + }; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&owner_or_assistant.pubkey()), + &[&owner_or_assistant], + test_context.last_blockhash, + ); + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + + let new_auction_state = current_state.auction_state().set_pause(is_paused); + + let expect_msg = format!( + "Failed to set {} auction state", + if is_paused { "pause" } else { "unpause" } + ); + current_state + .set_auction_state(new_auction_state) + .expect(&expect_msg) +} diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 6ffeddbd1..9d0debcba 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -28,6 +28,7 @@ use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; pub struct PrepareOrderResponseFixture { pub prepared_order_response: Pubkey, pub prepared_custody_token: Pubkey, + pub base_fee_token: Pubkey, } /// Prepare an order response (shimless) @@ -239,6 +240,7 @@ pub async fn prepare_order_response( Some(PrepareOrderResponseFixture { prepared_order_response: prepared_order_response_pda, prepared_custody_token: prepared_custody_token_pda, + base_fee_token: *base_fee_token_address, }) } else { None diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 3b819058f..1862110b9 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -1,5 +1,7 @@ use crate::testing_engine::config::ExpectedError; use crate::testing_engine::setup::TestingContext; +use crate::testing_engine::state::OrderPreparedState; +use crate::testing_engine::state::TestingEngineState; use crate::utils::auction::AuctionState; use anchor_lang::prelude::*; @@ -33,27 +35,34 @@ use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; /// The new auction state if successful, otherwise the old auction state pub async fn settle_auction_complete( testing_context: &TestingContext, + current_state: &TestingEngineState, test_context: &mut ProgramTestContext, payer_signer: &Rc, - auction_state: &AuctionState, - prepare_order_response_address: &Pubkey, - prepared_custody_token: &Pubkey, expected_error: Option<&ExpectedError>, ) -> AuctionState { + let auction_state = current_state.auction_state(); + let order_prepared_state = current_state + .order_prepared() + .expect("Order prepared not found"); + let OrderPreparedState { + prepared_order_response_address, + prepared_custody_token, + base_fee_token, + actor_enum: _, + } = *order_prepared_state; + let matching_engine_program_id = testing_context.get_matching_engine_program_id(); - let usdc_mint_address = &testing_context.get_usdc_mint_address(); let active_auction = auction_state .get_active_auction() .expect("Failed to get active auction"); - let base_fee_token = *usdc_mint_address; let event_seeds = EVENT_AUTHORITY_SEED; let event_authority = Pubkey::find_program_address(&[event_seeds], &matching_engine_program_id).0; let settle_auction_accounts = SettleAuctionCompleteCpiAccounts { beneficiary: payer_signer.pubkey(), base_fee_token, - prepared_order_response: *prepare_order_response_address, - prepared_custody_token: *prepared_custody_token, + prepared_order_response: prepared_order_response_address, + prepared_custody_token, auction: active_auction.auction_address, best_offer_token: active_auction.best_offer.offer_token, token_program: spl_token::ID, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 6479781a6..ebd36dabe 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -1,3 +1,9 @@ +// TODO: +// Shouldn't be able to improve offer after executing order +// Try within a grace period and after grace period to see if math checks out +// After grace period, try to execute order with different account to best offer to see if math checks out +// Check that the initial offer token being closed doesnt stop the execute order + //! # Execute order instruction testing //! //! This module contains tests for the execute order instruction. @@ -14,10 +20,14 @@ use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; -use crate::testing_engine::engine::{ExecutionChain, ExecutionTrigger, VerificationTrigger}; +use crate::testing_engine::engine::{ + fast_forward_slots, ExecutionChain, ExecutionTrigger, VerificationTrigger, +}; use crate::testing_engine::state::TestingEngineState; use crate::utils; +use crate::utils::token_account::SplTokenEnum; +use anchor_lang::error::ErrorCode; use solana_program_test::{tokio, ProgramTestContext}; use testing_engine::config::*; use testing_engine::engine::{InstructionTrigger, TestingEngine}; @@ -153,6 +163,212 @@ pub async fn test_execute_order_shimless_after_placing_initial_offer_with_shim() .await; } +/// Test executing order shim after grace period +#[tokio::test] +pub async fn test_execute_order_shim_after_grace_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_vec = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + ]; + let place_initial_offer_state = testing_engine + .execute(&mut test_context, instruction_vec, None) + .await; + fast_forward_slots(&mut test_context, 100).await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig::default(); + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim(execute_order_config)]; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let active_auction_state = execute_order_state + .auction_state() + .get_active_auction() + .unwrap(); + let executor_enum = execute_order_state.execute_order_actor().unwrap(); + let best_offer_enum = execute_order_state.best_offer_actor().unwrap(); + let initial_offer_enum = execute_order_state.initial_offer_placed_actor().unwrap(); + let execute_order_actor_enums = ExecuteOrderActorEnums { + executor: executor_enum, + best_offer: best_offer_enum, + initial_offer: initial_offer_enum, + }; + let verification_trigger = VerificationTrigger::VerifyBalances(VerifyBalancesConfig { + previous_state_balances, + balance_changes: BalanceChanges::execute_order_changes( + &mut test_context, + &execute_order_state, + executor_actor, + execute_order_actor_enums, + active_auction_state, + SplTokenEnum::Usdc, + ) + .await, + }); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test executing order shimless after grace period +#[tokio::test] +pub async fn test_execute_order_shimless_after_grace_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + fast_forward_slots(&mut test_context, 100).await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig::default(); + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let active_auction_state = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap(); + let executor_enum = TestingActorEnum::Solver(0); + let best_offer_enum = TestingActorEnum::Solver(0); + let initial_offer_enum = TestingActorEnum::Solver(0); + let execute_order_actor_enums = ExecuteOrderActorEnums { + executor: executor_enum, + best_offer: best_offer_enum, + initial_offer: initial_offer_enum, + }; + + let verification_trigger = VerificationTrigger::VerifyBalances(VerifyBalancesConfig { + previous_state_balances, + balance_changes: BalanceChanges::execute_order_changes( + &mut test_context, + &place_initial_offer_state, + executor_actor, + execute_order_actor_enums, + active_auction_state, + SplTokenEnum::Usdc, + ) + .await, + }); + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) + .await; +} + +/// Test executing order shim after custodian is paused after initial offer +#[tokio::test] +pub async fn test_execute_order_shim_after_custodian_is_paused_after_initial_offer() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::SetPauseCustodian( + SetPauseCustodianInstructionConfig { + is_paused: true, + ..Default::default() + }, + )]; + let paused_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( + ExecuteOrderInstructionConfig::default(), + )]; + testing_engine + .execute(&mut test_context, instruction_triggers, Some(paused_state)) + .await; +} + +/// Test executing order shimless after custodian is paused after initial offer +#[tokio::test] +pub async fn test_execute_order_shimless_after_custodian_is_paused_after_initial_offer() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::SetPauseCustodian( + SetPauseCustodianInstructionConfig { + is_paused: true, + ..Default::default() + }, + )]; + let paused_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + ExecuteOrderInstructionConfig::default(), + )]; + testing_engine + .execute(&mut test_context, instruction_triggers, Some(paused_state)) + .await; +} + /* Sad path tests section @@ -264,6 +480,37 @@ pub async fn test_execute_order_shim_after_close_fast_market_order_fails() { .await; } +/// Cannot improve offer after executing order +#[tokio::test] +pub async fn test_cannot_improve_offer_after_executing_order() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::ImproveOfferShimless(ImproveOfferInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(ErrorCode::AccountNotInitialized), + error_string: "AccountNotInitialized".to_string(), + }), + ..ImproveOfferInstructionConfig::default() + }), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + /* Helper code */ diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs index f4920fa3d..be742c124 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs @@ -116,17 +116,18 @@ pub async fn test_local_token_router_endpoint_creation() { None, ) .await; - + let payer_signer = testing_context.testing_actors.payer_signer.clone(); let initialize_fixture = initialize_program( &testing_context, &mut test_context, AuctionParametersConfig::default(), + &payer_signer, None, // No expected error None, // No expected log messages ) .await .expect("Failed to initialize program"); - let payer_signer = testing_context.testing_actors.owner.keypair(); + let payer_signer = testing_context.testing_actors.payer_signer.clone(); let _local_token_router_endpoint = add_local_router_endpoint_ix( &testing_context, &mut test_context, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index 919085fab..fed516860 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -1,5 +1,9 @@ #![allow(clippy::expect_used)] #![allow(clippy::panic)] +// TODO: +// Test that it is possible to continue to prepare order response and execution after initial offer is placed and is paused +// Test that auction is expired means that you cannot place offer or execute it +// Cannot place initial offer twice //! # Place initial offer and improve offer instruction testing //! @@ -466,6 +470,114 @@ pub async fn test_place_initial_offer_shim_fails_when_max_fee_and_amount_in_sum_ .await; } +/// Test place initial offer shim fails when vaa is expired +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_when_vaa_is_expired() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + let initialse_fast_market_order_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + testing_engine + .testing_context + .make_fast_transfer_vaa_expired(&mut test_context, 60) // 1 minute after expiry + .await; + + let place_initial_offer_config = PlaceInitialOfferInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::FastMarketOrderExpired), + error_string: "Fast market order has expired".to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }; + + let instruction_triggers = vec![InstructionTrigger::PlaceInitialOfferShim( + place_initial_offer_config, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initialse_fast_market_order_state), + ) + .await; +} + +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_custodian_is_paused() { + let transfer_direction = TransferDirection::FromArbitrumToEthereum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + let initial_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + + let pause_custodian_config = SetPauseCustodianInstructionConfig { + is_paused: true, + ..Default::default() + }; + let instruction_triggers = vec![InstructionTrigger::SetPauseCustodian( + pause_custodian_config, + )]; + let paused_state = testing_engine + .execute(&mut test_context, instruction_triggers, Some(initial_state)) + .await; + + let place_initial_offer_config = PlaceInitialOfferInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::Paused), + error_string: "Fast market order account owner is invalid".to_string(), + }), + ..PlaceInitialOfferInstructionConfig::default() + }; + let instruction_triggers = vec![InstructionTrigger::PlaceInitialOfferShim( + place_initial_offer_config, + )]; + testing_engine + .execute(&mut test_context, instruction_triggers, Some(paused_state)) + .await; +} + /// Test that improved offer fails when improvement is too small #[tokio::test] pub async fn test_improve_offer_shim_fails_carping() { diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index c9bf8314e..8b17544d8 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -114,3 +114,216 @@ pub async fn test_settle_auction_reopened_fast_market_order() { ) .await; } + +/// Test that the settle auction instruction results in the same balance changes for shim as non shim +#[tokio::test] +pub async fn test_settle_auction_balance_changes() { + // Run both tests and compare results + let balance_changes_shim = Box::pin(helpers::balance_changes_shim()).await; + let balance_changes_shimless = Box::pin(helpers::balance_changes_shimless()).await; + + // Compare results + helpers::compare_balance_changes(&balance_changes_shim, &balance_changes_shimless); +} + +mod helpers { + use super::*; + + pub async fn balance_changes_shim() -> BalanceChanges { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + + let testing_engine = TestingEngine::new(testing_context).await; + let initial_state_balances_shim = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + close_account_refund_recipient: Some( + testing_engine.testing_context.testing_actors.owner.pubkey(), + ), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + ]; + let place_initial_offer_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + let place_initial_offer_balances_shim = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + println!( + "place_initial_offer_balances_shim: {:?}", + place_initial_offer_balances_shim + .get(&TestingActorEnum::Solver(0)) + .unwrap() + .lamports + ); + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + ]; + let prepare_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let prepare_order_balances_shim = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + println!( + "prepare_order_balances_shim: {:?}", + prepare_order_balances_shim + .get(&TestingActorEnum::Solver(0)) + .unwrap() + .lamports + ); + let instruction_triggers = vec![InstructionTrigger::CloseFastMarketOrderShim( + CloseFastMarketOrderShimInstructionConfig { + close_account_refund_recipient_keypair: Some( + testing_engine + .testing_context + .testing_actors + .owner + .keypair(), + ), + ..CloseFastMarketOrderShimInstructionConfig::default() + }, + )]; + let close_fast_market_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepare_order_state), + ) + .await; + let close_fast_market_order_balances_shim = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + println!( + "close_fast_market_order_balances_shim: {:?}", + close_fast_market_order_balances_shim + .get(&TestingActorEnum::Solver(0)) + .unwrap() + .lamports + ); + let instruction_triggers = vec![InstructionTrigger::SettleAuction( + SettleAuctionInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(close_fast_market_order_state), + ) + .await; + let final_state_balances_shim = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + + BalanceChanges::from((&initial_state_balances_shim, &final_state_balances_shim)) + } + + pub async fn balance_changes_shimless() -> BalanceChanges { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let initial_state_balances_shimless = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless( + PlaceInitialOfferInstructionConfig::default(), + ), + ]; + let place_initial_offer_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + let place_initial_offer_balances_shimless = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + println!( + "place_initial_offer_balances_shimless: {:?}", + place_initial_offer_balances_shimless + .get(&TestingActorEnum::Owner) + .unwrap() + .lamports + ); + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let final_state_balances_shimless = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + BalanceChanges::from(( + &initial_state_balances_shimless, + &final_state_balances_shimless, + )) + } + + pub fn compare_balance_changes(shim: &BalanceChanges, shimless: &BalanceChanges) { + let shimless_owner_balance_change = + shimless.get(&TestingActorEnum::Owner).unwrap().lamports; + let shim_owner_balance_change = shim.get(&TestingActorEnum::Owner).unwrap().lamports; + let avg_cost_of_posting_vaa = 10_000_000; + + assert!( + shim_owner_balance_change >= shimless_owner_balance_change.saturating_sub(avg_cost_of_posting_vaa), + "Shim owner balance change should be greater than or equal to shimless owner balance change. Shim: {:?}, Shimless {:?}", + shim_owner_balance_change, + shimless_owner_balance_change + ); + assert_eq!( + shimless.get(&TestingActorEnum::Solver(0)).unwrap().usdc, + shim.get(&TestingActorEnum::Solver(0)).unwrap().usdc, + "Solver 0 balance change should be the same for both shim and shimless" + ); + } +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 57b541b36..3bbc48ffc 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -14,17 +14,22 @@ //! ]; //! ``` -use std::{collections::HashSet, rc::Rc}; +use std::{ + collections::{HashMap, HashSet}, + ops::Deref, + rc::Rc, +}; use crate::{ shimless::initialize::AuctionParametersConfig, - utils::{token_account::SplTokenEnum, Chain}, + utils::{auction::ActiveAuctionState, token_account::SplTokenEnum, Chain}, }; use anchor_lang::prelude::*; +use solana_program_test::ProgramTestContext; use solana_sdk::signature::Keypair; use super::{ - setup::{TestingActor, TestingActors}, + setup::{Balance, Balances, TestingActor, TestingActors}, state::TestingEngineState, }; @@ -67,6 +72,7 @@ pub struct ExpectedLog { #[derive(Clone, Default)] pub struct InitializeInstructionConfig { pub auction_parameters_config: AuctionParametersConfig, + pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, } @@ -126,10 +132,28 @@ impl InstructionConfig for InitializeFastMarketOrderShimInstructionConfig { } } +#[derive(Clone, Default)] +pub struct SetPauseCustodianInstructionConfig { + pub payer_signer: Option>, + pub is_paused: bool, + pub expected_error: Option, + pub expected_log_messages: Option>, +} + +impl InstructionConfig for SetPauseCustodianInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } +} + #[derive(Clone, Default)] pub struct PrepareOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, - pub solver_index: usize, + pub actor_enum: TestingActorEnum, + pub token_enum: SplTokenEnum, pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, @@ -147,7 +171,8 @@ impl InstructionConfig for PrepareOrderInstructionConfig { #[derive(Clone, Default)] pub struct ExecuteOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, - pub solver_index: usize, + pub actor_enum: TestingActorEnum, + pub token_enum: SplTokenEnum, pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, @@ -195,10 +220,14 @@ impl InstructionConfig for CloseFastMarketOrderShimInstructionConfig { } } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] pub enum TestingActorEnum { Solver(usize), Owner, + OwnerAssistant, + FeeRecipient, + Relayer, + Liquidator, } impl TestingActorEnum { @@ -206,6 +235,10 @@ impl TestingActorEnum { match self { Self::Solver(index) => testing_actors.solvers[*index].actor.clone(), Self::Owner => testing_actors.owner.clone(), + Self::OwnerAssistant => testing_actors.owner_assistant.clone(), + Self::FeeRecipient => testing_actors.fee_recipient.clone(), + Self::Relayer => testing_actors.relayer.clone(), + Self::Liquidator => testing_actors.liquidator.clone(), } } } @@ -338,3 +371,186 @@ impl InstructionConfig for ImproveOfferInstructionConfig { self.expected_log_messages.as_ref() } } + +pub struct VerifyBalancesConfig { + pub previous_state_balances: Balances, + pub balance_changes: BalanceChanges, +} + +pub struct ExecuteOrderActorEnums { + pub executor: TestingActorEnum, + pub best_offer: TestingActorEnum, + pub initial_offer: TestingActorEnum, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct BalanceChanges(HashMap); + +impl Deref for BalanceChanges { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<(&Balances, &Balances)> for BalanceChanges { + fn from((initial_balances, final_balances): (&Balances, &Balances)) -> Self { + let mut balance_changes = HashMap::new(); + + let all_actors: HashSet<_> = initial_balances + .keys() + .chain(final_balances.keys()) + .collect(); + + for actor in all_actors { + let initial = initial_balances + .get(actor) + .cloned() + .unwrap_or_else(|| Balance { + lamports: 0, + usdc: 0, + usdt: 0, + }); + + let final_bal = final_balances + .get(actor) + .cloned() + .unwrap_or_else(|| Balance { + lamports: 0, + usdc: 0, + usdt: 0, + }); + + let balance_change = BalanceChange { + lamports: i32::try_from( + i64::try_from(final_bal.lamports) + .unwrap() + .saturating_sub(i64::try_from(initial.lamports).unwrap()), + ) + .unwrap(), + usdc: i32::try_from( + i64::try_from(final_bal.usdc) + .unwrap() + .saturating_sub(i64::try_from(initial.usdc).unwrap()), + ) + .unwrap(), + usdt: i32::try_from( + i64::try_from(final_bal.usdt) + .unwrap() + .saturating_sub(i64::try_from(initial.usdt).unwrap()), + ) + .unwrap(), + }; + + balance_changes.insert(*actor, balance_change); + } + + Self(balance_changes) + } +} + +impl BalanceChanges { + pub async fn execute_order_changes( + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + executor: TestingActor, + execute_order_actor_enums: ExecuteOrderActorEnums, + active_auction_state: &ActiveAuctionState, + spl_token_enum: SplTokenEnum, + ) -> Self { + let ExecuteOrderActorEnums { + executor: executor_testing_actor_enum, + best_offer: best_offer_testing_actor_enum, + initial_offer: initial_offer_testing_actor_enum, + } = execute_order_actor_enums; + // TODO: Make this dynamic so that it does not depend on the first vaa pair + let fast_market_order = current_state + .base() + .get_fast_market_order(0) + .expect("Fast market order is not initialized"); + let init_auction_fee = fast_market_order.init_auction_fee; + let executor_token_address = executor.token_account_address(&spl_token_enum).unwrap(); + let auction_calculations = active_auction_state + .get_auction_calculations(test_context, executor_token_address, init_auction_fee) + .await; + println!("auction_calculations: {:?}", auction_calculations); + + let mut balance_changes = HashMap::new(); + balance_changes.insert( + executor_testing_actor_enum, + BalanceChange { + lamports: 0, + usdc: match spl_token_enum { + SplTokenEnum::Usdc => { + auction_calculations + .expected_token_balance_changes + .executor_token_balance_change + } + SplTokenEnum::Usdt => 0, + }, + usdt: match spl_token_enum { + SplTokenEnum::Usdc => 0, + SplTokenEnum::Usdt => { + auction_calculations + .expected_token_balance_changes + .executor_token_balance_change + } + }, + }, + ); + + balance_changes.insert( + best_offer_testing_actor_enum, + BalanceChange { + lamports: 0, + usdc: match spl_token_enum { + SplTokenEnum::Usdc => { + auction_calculations + .expected_token_balance_changes + .best_offer_token_balance_change + } + SplTokenEnum::Usdt => 0, + }, + usdt: match spl_token_enum { + SplTokenEnum::Usdc => 0, + SplTokenEnum::Usdt => { + auction_calculations + .expected_token_balance_changes + .best_offer_token_balance_change + } + }, + }, + ); + + balance_changes.insert( + initial_offer_testing_actor_enum, + BalanceChange { + lamports: 0, + usdc: match spl_token_enum { + SplTokenEnum::Usdc => { + auction_calculations + .expected_token_balance_changes + .initial_offer_token_balance_change + } + SplTokenEnum::Usdt => 0, + }, + usdt: match spl_token_enum { + SplTokenEnum::Usdc => 0, + SplTokenEnum::Usdt => { + auction_calculations + .expected_token_balance_changes + .initial_offer_token_balance_change + } + }, + }, + ); + Self(balance_changes) + } +} +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct BalanceChange { + pub lamports: i32, + pub usdc: i32, + pub usdt: i32, +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index f88081169..d9133571f 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -41,19 +41,14 @@ use crate::testing_engine::setup::ShimMode; use crate::utils::auction::AuctionState; use crate::utils::token_account::SplTokenEnum; use crate::utils::vaa::TestVaaPairs; -use crate::utils::{ - auction::{ - AuctionAccounts, - // AuctionState - }, - router::create_all_router_endpoints_test, -}; +use crate::utils::{auction::AuctionAccounts, router::create_all_router_endpoints_test}; use anchor_lang::prelude::*; pub enum InstructionTrigger { InitializeProgram(InitializeInstructionConfig), CreateCctpRouterEndpoints(CreateCctpRouterEndpointsInstructionConfig), InitializeFastMarketOrderShim(InitializeFastMarketOrderShimInstructionConfig), + SetPauseCustodian(SetPauseCustodianInstructionConfig), PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig), PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig), ImproveOfferShimless(ImproveOfferInstructionConfig), @@ -68,6 +63,8 @@ pub enum InstructionTrigger { pub enum VerificationTrigger { // Verify that the auction state is as expected (bool is expected to succeed) VerifyAuctionState(bool), + // Verify that the execute order math is correct + VerifyBalances(VerifyBalancesConfig), } pub enum ExecutionTrigger { @@ -104,6 +101,10 @@ impl DerefMut for ExecutionChain { } impl ExecutionChain { + pub fn new(triggers: Vec) -> Self { + Self(triggers) + } + pub fn instruction_triggers(&self) -> Vec<&InstructionTrigger> { self.iter() .filter_map(|trigger| { @@ -122,6 +123,12 @@ impl From> for ExecutionChain { } } +impl From> for ExecutionChain { + fn from(triggers: Vec) -> Self { + Self(triggers.into_iter().map(|trigger| trigger.into()).collect()) + } +} + impl InstructionTrigger { pub fn is_shim(&self) -> bool { matches!( @@ -141,6 +148,7 @@ impl InstructionConfig for InstructionTrigger { Self::InitializeProgram(config) => config.expected_error(), Self::CreateCctpRouterEndpoints(config) => config.expected_error(), Self::InitializeFastMarketOrderShim(config) => config.expected_error(), + Self::SetPauseCustodian(config) => config.expected_error(), Self::PlaceInitialOfferShimless(config) => config.expected_error(), Self::PlaceInitialOfferShim(config) => config.expected_error(), Self::ImproveOfferShimless(config) => config.expected_error(), @@ -157,6 +165,7 @@ impl InstructionConfig for InstructionTrigger { Self::InitializeProgram(config) => config.expected_log_messages(), Self::CreateCctpRouterEndpoints(config) => config.expected_log_messages(), Self::InitializeFastMarketOrderShim(config) => config.expected_log_messages(), + Self::SetPauseCustodian(config) => config.expected_log_messages(), Self::PlaceInitialOfferShimless(config) => config.expected_log_messages(), Self::PlaceInitialOfferShim(config) => config.expected_log_messages(), Self::ImproveOfferShimless(config) => config.expected_log_messages(), @@ -294,6 +303,10 @@ impl TestingEngine { self.close_fast_market_order_account(test_context, current_state, config) .await } + InstructionTrigger::SetPauseCustodian(ref config) => { + self.set_pause_custodian(test_context, current_state, config) + .await + } InstructionTrigger::PlaceInitialOfferShimless(ref config) => { self.place_initial_offer_shimless(test_context, current_state, config) .await @@ -332,6 +345,10 @@ impl TestingEngine { self.verify_auction_state(test_context, current_state, expected_to_succeed) .await } + VerificationTrigger::VerifyBalances(ref config) => { + self.verify_balances(test_context, current_state, config) + .await + } }, } } @@ -362,12 +379,16 @@ impl TestingEngine { let auction_parameters_config = config.auction_parameters_config.clone(); let expected_error = config.expected_error(); let expected_log_messages = config.expected_log_messages(); - + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { let result = shimless::initialize::initialize_program( &self.testing_context, test_context, auction_parameters_config, + &payer_signer, expected_error, expected_log_messages, ) @@ -460,7 +481,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let guardian_signature_info = create_guardian_signatures( &self.testing_context, test_context, @@ -517,6 +538,32 @@ impl TestingEngine { } } + /// Instruction trigger function for pausing the custodian + async fn set_pause_custodian( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &SetPauseCustodianInstructionConfig, + ) -> TestingEngineState { + let owner_or_assistant = config.payer_signer.clone().unwrap_or_else(|| { + self.testing_context + .testing_actors + .owner_assistant + .keypair() + }); + let is_paused = config.is_paused; + let testing_context = &self.testing_context; + shimless::pause_custodian::set_pause( + test_context, + testing_context, + current_state, + &owner_or_assistant, + config.expected_error(), + is_paused, + ) + .await + } + /// Instruction trigger function for closing a fast market order account async fn close_fast_market_order_account( &self, @@ -553,6 +600,7 @@ impl TestingEngine { fast_market_order: current_state.fast_market_order().cloned(), order_prepared: current_state.order_prepared().cloned(), auction_accounts: current_state.auction_accounts().cloned(), + order_executed: current_state.order_executed().cloned(), } } async fn place_initial_offer_shimless( @@ -607,7 +655,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let new_auction_state = shimless::make_offer::improve_offer( &self.testing_context, test_context, @@ -693,6 +741,7 @@ impl TestingEngine { cctp_message: order_executed_fallback_fixture.cctp_message, post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), post_message_message: Some(order_executed_fallback_fixture.post_message_message), + actor_enum: config.actor_enum, }; TestingEngineState::OrderExecuted { base: current_state.base().clone(), @@ -718,14 +767,16 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_config_address = current_state .auction_config_address() .expect("Auction config address not found"); let router_endpoints = current_state .router_endpoints() .expect("Router endpoints are not created"); - let solver = self.testing_context.testing_actors.solvers[config.solver_index].clone(); + let actor = config + .actor_enum + .get_actor(&self.testing_context.testing_actors); let custodian_address = current_state .custodian_address() .expect("Custodian address not found"); @@ -736,7 +787,7 @@ impl TestingEngine { .fast_transfer_vaa .get_vaa_pubkey(), ), - solver.actor.clone(), + actor.clone(), current_state.close_account_refund_recipient(), auction_config_address, &router_endpoints.endpoints, @@ -759,6 +810,7 @@ impl TestingEngine { cctp_message: execute_order_fixture.cctp_message, post_message_sequence: None, post_message_message: None, + actor_enum: config.actor_enum, }; TestingEngineState::OrderExecuted { base: current_state.base().clone(), @@ -797,7 +849,7 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let result = shimful::shims_prepare_order_response::prepare_order_response_test( &self.testing_context, @@ -814,8 +866,11 @@ impl TestingEngine { if config.expected_error.is_none() { let prepare_order_response_fixture = result.unwrap(); let order_prepared_state = OrderPreparedState { - prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_order_response_address: prepare_order_response_fixture + .prepared_order_response, prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + base_fee_token: prepare_order_response_fixture.base_fee_token, + actor_enum: config.actor_enum, }; TestingEngineState::OrderPrepared { base: current_state.base().clone(), @@ -841,18 +896,16 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_accounts = current_state .auction_accounts() .expect("Auction accounts not found"); - let solver_token_account = self - .testing_context - .testing_actors - .solvers - .get(config.solver_index) - .expect("Solver not found at index") - .token_account_address() + let solver_token_account = config + .actor_enum + .get_actor(&self.testing_context.testing_actors) + .token_account_address(&config.token_enum) .expect("Token account does not exist for solver at index"); + println!("Base fee token address: {:?}", solver_token_account); let result = shimless::prepare_order_response::prepare_order_response( &self.testing_context, test_context, @@ -868,8 +921,11 @@ impl TestingEngine { if config.expected_error.is_none() { let prepare_order_response_fixture = result.unwrap(); let order_prepared_state = OrderPreparedState { - prepared_order_address: prepare_order_response_fixture.prepared_order_response, + prepared_order_response_address: prepare_order_response_fixture + .prepared_order_response, prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, + base_fee_token: prepare_order_response_fixture.base_fee_token, + actor_enum: config.actor_enum, }; TestingEngineState::OrderPrepared { base: current_state.base().clone(), @@ -895,19 +951,12 @@ impl TestingEngine { let payer_signer = config .payer_signer .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.owner.keypair()); - let order_prepared_state = current_state - .order_prepared() - .expect("Order prepared not found"); - let prepared_custody_token = order_prepared_state.prepared_custody_token; - let prepared_order_response = order_prepared_state.prepared_order_address; + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_state = shimless::settle_auction::settle_auction_complete( &self.testing_context, + current_state, test_context, &payer_signer, - current_state.auction_state(), - &prepared_order_response, - &prepared_custody_token, config.expected_error(), ) .await; @@ -918,8 +967,9 @@ impl TestingEngine { router_endpoints: current_state.router_endpoints().unwrap().clone(), auction_state: current_state.auction_state().clone(), fast_market_order: current_state.fast_market_order().cloned(), - order_prepared: order_prepared_state.clone(), + order_prepared: current_state.order_prepared().unwrap().clone(), auction_accounts: current_state.auction_accounts().cloned(), + order_executed: current_state.order_executed().cloned(), }, _ => current_state.clone(), } @@ -942,6 +992,33 @@ impl TestingEngine { assert_eq!(was_success, expected_to_succeed); current_state.clone() } + + async fn verify_balances( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &VerifyBalancesConfig, + ) -> TestingEngineState { + let previous_state_balances = &config.previous_state_balances; + let balances = self.testing_context.get_balances(test_context).await; + for (actor, balance_change) in config.balance_changes.iter() { + let balance = balances.get(actor).unwrap(); + let previous_balance = previous_state_balances.get(actor).unwrap(); + assert_eq!( + balance.usdc, + saturating_add_signed(previous_balance.usdc, balance_change.usdc), + "USDC balance mismatch for actor {:?}", + actor + ); + assert_eq!( + balance.usdt, + saturating_add_signed(previous_balance.usdt, balance_change.usdt), + "USDT balance mismatch for actor {:?}", + actor + ); + } + current_state.clone() + } } /// Fast forwards the slot in the test context @@ -981,3 +1058,12 @@ pub async fn fast_forward_slots(test_context: &mut ProgramTestContext, num_slots println!("Fast forwarded {} slots", num_slots); } + +#[allow(clippy::cast_sign_loss)] +fn saturating_add_signed(unsigned: u64, signed: i32) -> u64 { + if signed >= 0 { + unsigned.saturating_add(signed as u64) + } else { + unsigned.saturating_sub(signed.unsigned_abs() as u64) + } +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 402e183e0..c93de196e 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -36,6 +36,7 @@ use anchor_spl::token::{ use anyhow::Result as AnyhowResult; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::{BanksClientError, ProgramTest, ProgramTestContext}; +use solana_sdk::clock::Clock; use solana_sdk::instruction::InstructionError; use solana_sdk::transaction::{TransactionError, VersionedTransaction}; use solana_sdk::{ @@ -43,8 +44,12 @@ use solana_sdk::{ signature::{Keypair, Signer}, transaction::Transaction, }; +use std::collections::HashMap; +use std::ops::Deref; use std::rc::Rc; +use super::config::TestingActorEnum; + // Configures the program ID and CCTP mint recipient based on the environment cfg_if::cfg_if! { if #[cfg(feature = "mainnet")] { @@ -99,12 +104,13 @@ impl PreTestingContext { program_id, None, ); + program_test.set_compute_max_units(1000000000); program_test.set_transaction_account_lock_limit(1000); // Setup Testing Actors let testing_actors = TestingActors::new(owner_keypair_path); - + println!("Testing actors: {:?}", testing_actors); // Initialise Upgrade Manager let program_data_pubkey = initialise_upgrade_manager( &mut program_test, @@ -415,6 +421,49 @@ impl TestingContext { ); } } + + /// Gets the balances of all the test actors + pub async fn get_balances(&self, test_context: &mut ProgramTestContext) -> Balances { + Balances::new(&self.testing_actors, test_context).await + } + + pub async fn get_current_timestamp(&self, test_context: &mut ProgramTestContext) -> i64 { + let clock = test_context + .banks_client + .get_sysvar::() + .await + .expect("Failed to get clock sysvar"); + clock.unix_timestamp + } + + pub async fn fast_forward_to_timestamp( + &self, + test_context: &mut ProgramTestContext, + target_timestamp: i64, + ) { + let new_clock = Clock { + unix_timestamp: target_timestamp, + ..Default::default() + }; + test_context.set_sysvar(&new_clock); + let current_timestamp = self.get_current_timestamp(test_context).await; + assert!(current_timestamp >= target_timestamp); + } + + pub async fn make_fast_transfer_vaa_expired( + &self, + test_context: &mut ProgramTestContext, + seconds_after_expiry: i64, // Make this negative if you want it slightly before expiry + ) { + let vaa_expiration_time = i64::from( + self.get_vaa_pair(0) + .unwrap() + .get_fast_transfer_vaa_expiration_time(), + ); + let target_timestamp = vaa_expiration_time + seconds_after_expiry; + self.fast_forward_to_timestamp(test_context, target_timestamp) + .await; + } } /// A struct representing a solver @@ -608,8 +657,84 @@ impl TestingActor { } } +/// A struct containing the balances of all the test actors +#[derive(Debug, Clone)] +pub struct Balances(HashMap); + +impl Deref for Balances { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Balances { + pub fn get(&self, actor: &TestingActorEnum) -> Option<&Balance> { + self.0.get(actor) + } +} + +impl Balances { + pub async fn new( + testing_actors: &TestingActors, + test_context: &mut ProgramTestContext, + ) -> Self { + let mut balances = HashMap::new(); + balances.insert( + TestingActorEnum::Owner, + Balance::new(&testing_actors.owner, test_context).await, + ); + balances.insert( + TestingActorEnum::OwnerAssistant, + Balance::new(&testing_actors.owner_assistant, test_context).await, + ); + balances.insert( + TestingActorEnum::FeeRecipient, + Balance::new(&testing_actors.fee_recipient, test_context).await, + ); + balances.insert( + TestingActorEnum::Relayer, + Balance::new(&testing_actors.relayer, test_context).await, + ); + for (index, solver) in testing_actors.solvers.iter().enumerate() { + balances.insert( + TestingActorEnum::Solver(index), + Balance::new(&solver.actor, test_context).await, + ); + } + balances.insert( + TestingActorEnum::Liquidator, + Balance::new(&testing_actors.liquidator, test_context).await, + ); + Self(balances) + } +} + +#[derive(Default, Debug, Clone)] +pub struct Balance { + pub lamports: u64, + pub usdc: u64, + pub usdt: u64, +} + +impl Balance { + pub async fn new(testing_actor: &TestingActor, test_context: &mut ProgramTestContext) -> Self { + Self { + lamports: testing_actor.get_lamport_balance(test_context).await, + usdc: testing_actor + .get_token_account_balance(test_context, &SplTokenEnum::Usdc) + .await, + usdt: testing_actor + .get_token_account_balance(test_context, &SplTokenEnum::Usdt) + .await, + } + } +} + /// A struct containing all the testing actors (the owner, the owner assistant, the fee recipient, the relayer, solvers, liquidator) pub struct TestingActors { + pub payer_signer: Rc, pub owner: TestingActor, pub owner_assistant: TestingActor, pub fee_recipient: TestingActor, @@ -618,6 +743,32 @@ pub struct TestingActors { pub liquidator: TestingActor, } +impl std::fmt::Debug for TestingActors { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Create a string that lists all solvers with their indices + let solver_string = { + let solver_entries: Vec = self + .solvers + .iter() + .enumerate() // This gives (index, value) pairs + .map(|(i, solver)| format!("solver {}: {}", i, solver.pubkey())) + .collect(); + + format!("[{}]", solver_entries.join(", ")) + }; + write!( + f, + "TestingActors {{ owner: {:?}, owner_assistant: {:?}, fee_recipient: {:?}, relayer: {:?}, solvers: {:?}, liquidator: {:?} }}", + self.owner.pubkey(), + self.owner_assistant.pubkey(), + self.fee_recipient.pubkey(), + self.relayer.pubkey(), + solver_string, + self.liquidator.pubkey(), + ) + } +} + impl TestingActors { /// Create a new TestingActors struct /// @@ -640,6 +791,7 @@ impl TestingActors { ]); let liquidator = TestingActor::new(Rc::new(Keypair::new()), None, None); Self { + payer_signer: Rc::new(Keypair::new()), owner, owner_assistant, fee_recipient, @@ -663,6 +815,7 @@ impl TestingActors { /// Transfer Lamports to Executors async fn airdrop_all(&self, test_context: &mut ProgramTestContext) { + airdrop(test_context, &self.payer_signer.pubkey(), 10000000000).await; airdrop(test_context, &self.owner.pubkey(), 10000000000).await; airdrop(test_context, &self.owner_assistant.pubkey(), 10000000000).await; airdrop(test_context, &self.fee_recipient.pubkey(), 10000000000).await; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index a74218550..8613dac96 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -12,7 +12,7 @@ //! // Use the testing engine state to test the instructions and move through the states //! ``` -use super::setup::TransferDirection; +use super::{config::TestingActorEnum, setup::TransferDirection}; use crate::utils::{ account_fixtures::FixtureAccounts, auction::{AuctionAccounts, AuctionState}, @@ -31,6 +31,18 @@ pub struct BaseState { pub transfer_direction: TransferDirection, } +impl BaseState { + pub fn get_fast_market_order(&self, index: usize) -> Option { + self.vaas.get(index).map(|vaa| { + vaa.fast_transfer_vaa + .get_payload_deserialized() + .unwrap() + .get_fast_transfer() + .unwrap() + }) + } +} + // Each state contains its specific data #[derive(Clone)] pub struct InitializedState { @@ -67,12 +79,15 @@ pub struct OrderExecutedState { pub cctp_message: Pubkey, pub post_message_sequence: Option, // Only set if shimful execution pub post_message_message: Option, // Only set if shimful execution + pub actor_enum: TestingActorEnum, } #[derive(Clone)] pub struct OrderPreparedState { - pub prepared_order_address: Pubkey, + pub prepared_order_response_address: Pubkey, pub prepared_custody_token: Pubkey, + pub base_fee_token: Pubkey, + pub actor_enum: TestingActorEnum, } #[derive(Clone)] @@ -82,7 +97,6 @@ pub struct GuardianSetState { } // The main state enum that reflects all possible instruction states -#[allow(dead_code)] #[derive(Clone)] pub enum TestingEngineState { Uninitialized(BaseState), @@ -146,6 +160,7 @@ pub enum TestingEngineState { fast_market_order: Option, order_prepared: OrderPreparedState, auction_accounts: Option, + order_executed: Option, }, FastMarketOrderClosed { base: BaseState, @@ -155,6 +170,7 @@ pub enum TestingEngineState { fast_market_order: Option, order_prepared: Option, auction_accounts: Option, + order_executed: Option, }, } @@ -292,6 +308,23 @@ impl TestingEngineState { } } + pub fn initial_offer_placed_actor(&self) -> Option { + self.auction_state() + .get_active_auction() + .map(|auction| auction.initial_offer.actor) + } + + pub fn best_offer_actor(&self) -> Option { + self.auction_state() + .get_active_auction() + .map(|auction| auction.best_offer.actor) + } + + pub fn execute_order_actor(&self) -> Option { + self.order_executed() + .map(|order_executed| order_executed.actor_enum) + } + // Prepared order accessor pub fn order_prepared(&self) -> Option<&OrderPreparedState> { match self { @@ -302,6 +335,14 @@ impl TestingEngineState { } } + pub fn order_executed(&self) -> Option<&OrderExecutedState> { + match self { + Self::AuctionSettled { order_executed, .. } => order_executed.as_ref(), + Self::OrderExecuted { order_executed, .. } => Some(order_executed), + _ => None, + } + } + pub fn get_first_test_vaa_pair(&self) -> &TestVaaPair { self.base().vaas.first().unwrap() } @@ -324,4 +365,137 @@ impl TestingEngineState { self.fast_market_order() .map(|fast_market_order| fast_market_order.close_account_refund_recipient) } + + pub fn set_auction_state(&self, new_auction_state: AuctionState) -> anyhow::Result { + match self { + Self::FastMarketOrderAccountCreated { + base, + initialized, + router_endpoints, + fast_market_order, + guardian_set_state, + auction_state: _, // Ignore the current auction state + auction_accounts, + } => Ok(Self::FastMarketOrderAccountCreated { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + fast_market_order: fast_market_order.clone(), + guardian_set_state: guardian_set_state.clone(), + auction_state: new_auction_state, // Use the new auction state + auction_accounts: auction_accounts.clone(), + }), + + Self::InitialOfferPlaced { + base, + initialized, + router_endpoints, + fast_market_order, + auction_state: _, // Ignore the current auction state + auction_accounts, + } => Ok(Self::InitialOfferPlaced { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + fast_market_order: fast_market_order.clone(), + auction_state: new_auction_state, // Use the new auction state + auction_accounts: auction_accounts.clone(), + }), + + Self::OfferImproved { + base, + initialized, + router_endpoints, + fast_market_order, + auction_state: _, // Ignore the current auction state + auction_accounts, + } => Ok(Self::OfferImproved { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + fast_market_order: fast_market_order.clone(), + auction_state: new_auction_state, // Use the new auction state + auction_accounts: auction_accounts.clone(), + }), + + Self::OrderExecuted { + base, + initialized, + router_endpoints, + fast_market_order, + auction_state: _, // Ignore the current auction state + order_executed, + auction_accounts, + } => Ok(Self::OrderExecuted { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + fast_market_order: fast_market_order.clone(), + auction_state: new_auction_state, // Use the new auction state + order_executed: order_executed.clone(), + auction_accounts: auction_accounts.clone(), + }), + + Self::OrderPrepared { + base, + initialized, + router_endpoints, + fast_market_order, + auction_state: _, // Ignore the current auction state + order_prepared, + auction_accounts, + } => Ok(Self::OrderPrepared { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + fast_market_order: fast_market_order.clone(), + auction_state: new_auction_state, // Use the new auction state + order_prepared: order_prepared.clone(), + auction_accounts: auction_accounts.clone(), + }), + + Self::AuctionSettled { + base, + initialized, + router_endpoints, + auction_state: _, // Ignore the current auction state + fast_market_order, + order_prepared, + auction_accounts, + order_executed, + } => Ok(Self::AuctionSettled { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + auction_state: new_auction_state, // Use the new auction state + fast_market_order: fast_market_order.clone(), + order_prepared: order_prepared.clone(), + auction_accounts: auction_accounts.clone(), + order_executed: order_executed.clone(), + }), + + Self::FastMarketOrderClosed { + base, + initialized, + router_endpoints, + auction_state: _, // Ignore the current auction state + fast_market_order, + order_prepared, + auction_accounts, + order_executed, + } => Ok(Self::FastMarketOrderClosed { + base: base.clone(), + initialized: initialized.clone(), + router_endpoints: router_endpoints.clone(), + auction_state: new_auction_state, // Use the new auction state + fast_market_order: fast_market_order.clone(), + order_prepared: order_prepared.clone(), + auction_accounts: auction_accounts.clone(), + order_executed: order_executed.clone(), + }), + + // For states that don't have an auction_state field + _ => anyhow::bail!("Cannot set auction state for this state: no auction state exists"), + } + } } diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 385b7168a..6299dc121 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -1,11 +1,13 @@ use anchor_lang::prelude::*; +use anchor_spl::token::TokenAccount; use solana_program_test::ProgramTestContext; use super::Chain; use super::{router::TestRouterEndpoints, token_account::SplTokenEnum}; +use crate::testing_engine::config::TestingActorEnum; use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; use anyhow::{anyhow, ensure, Result as AnyhowResult}; -use matching_engine::state::{Auction, AuctionInfo}; +use matching_engine::state::{Auction, AuctionConfig, AuctionInfo}; /// A struct representing the accounts for an auction /// @@ -42,6 +44,7 @@ pub struct AuctionAccounts { #[derive(Clone)] pub enum AuctionState { Active(Box), + Paused(Box), Settled, Inactive, } @@ -50,10 +53,31 @@ impl AuctionState { pub fn get_active_auction(&self) -> Option<&ActiveAuctionState> { match self { AuctionState::Active(auction) => Some(auction), + AuctionState::Paused(auction) => Some(auction), AuctionState::Inactive => None, AuctionState::Settled => None, } } + + pub fn set_pause(&self, is_paused: bool) -> Self { + match self { + AuctionState::Active(auction) => { + if is_paused { + AuctionState::Paused(auction.clone()) + } else { + AuctionState::Active(auction.clone()) + } + } + AuctionState::Paused(auction) => { + if is_paused { + AuctionState::Paused(auction.clone()) + } else { + AuctionState::Active(auction.clone()) + } + } + _ => self.clone(), + } + } } /// A struct representing an active auction @@ -75,6 +99,257 @@ pub struct ActiveAuctionState { pub spl_token_enum: SplTokenEnum, } +#[derive(Debug)] +pub struct ExpectedTokenBalanceChanges { + pub executor_token_balance_change: i32, + pub best_offer_token_balance_change: i32, + pub initial_offer_token_balance_change: i32, +} + +/// A struct representing the calculations for an auction +#[derive(Debug)] +pub struct AuctionCalculations { + pub penalty_amount: i32, + pub user_reward: i32, + pub security_deposit: i32, + pub init_auction_fee: i32, + pub min_offer_delta: u64, + pub notional_security_deposit: u64, + pub amount_in: i32, // Expose for easy access + pub deposit_and_fee: i32, + pub custody_token_balance_change: i32, + pub expected_token_balance_changes: ExpectedTokenBalanceChanges, + pub has_penalty: bool, +} + +impl ActiveAuctionState { + pub const BPS_DENOMINATOR: u64 = 1_000_000; + /// Computes the penalty amount and user reward for the auction + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + pub async fn get_auction_calculations( + &self, + test_context: &mut ProgramTestContext, + executor_token_address: Pubkey, + init_auction_fee: u64, + ) -> AuctionCalculations { + let auction_info = helpers::get_auction_info(test_context, self.auction_address).await; + let auction_config = + helpers::get_auction_config(test_context, self.auction_config_address).await; + + let best_offer_token_account_exists = + helpers::token_account_exists(test_context, self.best_offer.offer_token).await; + + let initial_offer_token_account_exists = + helpers::token_account_exists(test_context, self.initial_offer.offer_token).await; + + let custody_token_balance = + helpers::get_token_account_balance(test_context, self.auction_custody_token_address) + .await; + + // Cast to u64 for math later + let amount_in = auction_info.amount_in; + let grace_period = u64::from(auction_config.grace_period); + let initial_penalty_bps = u64::from(auction_config.initial_penalty_bps); + let penalty_period = u64::from(auction_config.penalty_period); + let user_penalty_reward_bps = u64::from(auction_config.user_penalty_reward_bps); + let security_deposit = auction_info.security_deposit; + let min_offer_delta_bps = u64::from(auction_config.min_offer_delta_bps); + let security_deposit_bps = u64::from(auction_config.security_deposit_bps); + + let latest_slot = test_context.banks_client.get_root_slot().await.unwrap(); + let slots_elapsed = latest_slot.saturating_sub(auction_info.start_slot); + let elapsed_penalty_period = slots_elapsed.saturating_sub(grace_period); + let has_penalty = elapsed_penalty_period >= penalty_period; + + // Copy of computeDepositPenalty + let (penalty_amount, user_reward) = if has_penalty { + if elapsed_penalty_period >= penalty_period + || initial_penalty_bps == Self::BPS_DENOMINATOR + { + let user_reward = security_deposit + .checked_mul(user_penalty_reward_bps) + .unwrap() + .checked_div(Self::BPS_DENOMINATOR) + .unwrap(); // security_deposit * user_penalty_reward_bps / BPS_DENOMINATOR + ( + security_deposit.checked_sub(user_reward).unwrap(), // security_deposit - user_reward + user_reward, // user_reward + ) + } else { + let base_penalty = security_deposit + .checked_mul(initial_penalty_bps) + .unwrap() + .checked_div(Self::BPS_DENOMINATOR) + .unwrap(); // base_penalty = security_deposit * initial_penalty_bps / 10000 + let penalty_period_elapsed_penalty = security_deposit + .checked_sub(base_penalty) + .unwrap() + .checked_mul(elapsed_penalty_period) + .unwrap() + .checked_div(penalty_period) + .unwrap(); // (security_deposit - base_penalty) * elapsed_penalty_period / penalty_period + let pre_penalty_amount = base_penalty + .checked_add(penalty_period_elapsed_penalty) + .unwrap(); + let user_reward = pre_penalty_amount + .checked_mul(user_penalty_reward_bps) + .unwrap() + .checked_div(Self::BPS_DENOMINATOR) + .unwrap(); + ( + pre_penalty_amount.checked_sub(user_reward).unwrap(), + user_reward, + ) + } + } else { + (0, 0) + }; + + let min_offer_delta = self + .best_offer + .offer_price + .checked_mul(min_offer_delta_bps) + .unwrap() + .checked_div(Self::BPS_DENOMINATOR) + .unwrap(); + let notional_security_deposit = amount_in + .checked_mul(security_deposit_bps) + .unwrap() + .checked_div(Self::BPS_DENOMINATOR) + .unwrap(); + + let mut executor_token_balance_change: i32 = 0; + let mut best_offer_token_balance_change: i32 = 0; + let mut initial_offer_token_balance_change: i32 = 0; + + let mut deposit_and_fee = if has_penalty { + i32::try_from(security_deposit.saturating_sub(user_reward)).unwrap() + } else { + i32::try_from(security_deposit.saturating_add(self.best_offer.offer_price)).unwrap() + }; + + // Cast to i32 for math later + let penalty_amount = i32::try_from(penalty_amount).unwrap(); + let user_reward = i32::try_from(user_reward).unwrap(); + let security_deposit = i32::try_from(security_deposit).unwrap(); + let offer_price = i32::try_from(auction_info.offer_price).unwrap(); + let amount_in = i32::try_from(amount_in).unwrap(); + let init_auction_fee = i32::try_from(init_auction_fee).unwrap(); + + // Helper function to calculate the custody token balance change + let new_custody_token_balance_calc = + |custody_token_balance: u64, custody_token_balance_change: i32| { + custody_token_balance.saturating_add_signed(custody_token_balance_change as i64) + as i32 + }; + + // Find the custody token balance change + + // custody_token_balance_change = init_auction_fee + offer_price - amount_in + let mut custody_token_balance_change = init_auction_fee + .saturating_add(offer_price) + .saturating_sub(amount_in); + + // If the best offer token is not the same as the initial offer token, and the initial offer token account exists, subtract the init auction fee + if self.best_offer.offer_token != self.initial_offer.offer_token + && initial_offer_token_account_exists + { + custody_token_balance_change = + custody_token_balance_change.saturating_sub(init_auction_fee); + } + + // If there is a penalty + if has_penalty { + // Subtract the user reward + custody_token_balance_change = custody_token_balance_change.saturating_sub(user_reward); + + // If the executor token is the same as the best offer token, the custody token balance is given to the executor + if executor_token_address == self.best_offer.offer_token { + let balance_change = new_custody_token_balance_calc( + custody_token_balance, + custody_token_balance_change, + ); + executor_token_balance_change = balance_change; + best_offer_token_balance_change = balance_change; + + // If the all token accounts are the same, apply the same balance change to each of them + if self.initial_offer.offer_token == self.best_offer.offer_token { + initial_offer_token_balance_change = balance_change; + } + + // If there is a penalty and the executor token is not the same as the best offer token + } else { + // Subtract the penalty amount from the deposit and fee + deposit_and_fee = deposit_and_fee.saturating_sub(penalty_amount); + + // If the best offer token account exists, subtract the deposit and fee from the custody token balance change + if best_offer_token_account_exists { + custody_token_balance_change = + custody_token_balance_change.saturating_sub(deposit_and_fee); + } + + // The remaining balance is given to the executor + executor_token_balance_change = new_custody_token_balance_calc( + custody_token_balance, + custody_token_balance_change, + ); + + // If the initial offer token is the same as the best offer token, apply the same balance change to each of them + if self.initial_offer.offer_token == self.best_offer.offer_token { + let balance_change = + deposit_and_fee + init_auction_fee + amount_in + security_deposit; + // This is sufficient, because either neither of them exist or both do + if best_offer_token_account_exists { + best_offer_token_balance_change = balance_change; + initial_offer_token_balance_change = balance_change; + }; + } else { + if best_offer_token_account_exists { + best_offer_token_balance_change = deposit_and_fee; + }; + if initial_offer_token_account_exists { + initial_offer_token_balance_change = init_auction_fee; + } + } + } + // If there is no penalty + } else if self.best_offer.offer_token == self.initial_offer.offer_token { + let balance_change = deposit_and_fee + init_auction_fee; + best_offer_token_balance_change = balance_change; + initial_offer_token_balance_change = balance_change; + } else { + best_offer_token_balance_change = deposit_and_fee; + initial_offer_token_balance_change = init_auction_fee; + } + + let expected_token_balance_changes = ExpectedTokenBalanceChanges { + executor_token_balance_change, + best_offer_token_balance_change, + initial_offer_token_balance_change, + }; + + AuctionCalculations { + penalty_amount, + user_reward, + security_deposit, + init_auction_fee, + min_offer_delta, + notional_security_deposit, + amount_in, + deposit_and_fee, + custody_token_balance_change, + expected_token_balance_changes, + has_penalty, + } + } +} + /// A struct representing an auction offer /// /// # Fields @@ -84,6 +359,7 @@ pub struct ActiveAuctionState { /// * `offer_price` - The price of the offer #[derive(Clone)] pub struct AuctionOffer { + pub actor: TestingActorEnum, pub participant: Pubkey, pub offer_token: Pubkey, pub offer_price: u64, @@ -238,3 +514,70 @@ pub async fn compare_auctions(auction_1: &Auction, auction_2: &Auction) { assert_eq!(auction_1_info.start_slot, auction_2_info.start_slot); assert_eq!(auction_1_info.offer_price, auction_2_info.offer_price); } + +mod helpers { + use super::*; + + pub async fn token_account_exists( + test_context: &mut ProgramTestContext, + token_address: Pubkey, + ) -> bool { + if let Some(account) = test_context + .banks_client + .get_account(token_address) + .await + .unwrap() + { + TokenAccount::try_deserialize(&mut &account.data[..]).is_ok() + } else { + false + } + } + + pub async fn get_auction_config( + test_context: &mut ProgramTestContext, + auction_config_address: Pubkey, + ) -> AuctionConfig { + let auction_config = test_context + .banks_client + .get_account(auction_config_address) + .await + .unwrap() + .unwrap(); + let mut data_ref = auction_config.data.as_ref(); + let auction_config_data: AuctionConfig = + AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + auction_config_data + } + + pub async fn get_auction_info( + test_context: &mut ProgramTestContext, + auction_address: Pubkey, + ) -> AuctionInfo { + let auction = test_context + .banks_client + .get_account(auction_address) + .await + .unwrap() + .unwrap(); + let mut data_ref = auction.data.as_ref(); + let auction_data: Auction = AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + auction_data.info.unwrap() + } + + pub async fn get_token_account_balance( + test_context: &mut ProgramTestContext, + token_address: Pubkey, + ) -> u64 { + let token_account = test_context + .banks_client + .get_account(token_address) + .await + .unwrap() + .unwrap(); + let mut data_ref = token_account.data.as_ref(); + let token_account_data: TokenAccount = + AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + token_account_data.amount + } +} diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index 3821531a8..94605788d 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -42,7 +42,7 @@ impl std::fmt::Debug for TokenAccountFixture { /// /// The token account fixture pub async fn create_token_account( - test_ctx: &mut ProgramTestContext, + test_context: &mut ProgramTestContext, owner: &Keypair, mint: &Pubkey, ) -> TokenAccountFixture { @@ -55,24 +55,28 @@ pub async fn create_token_account( // Create instruction using borrowed values let create_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( - &test_ctx.payer.pubkey(), // Funding account - &owner.pubkey(), // Wallet address - mint, // Mint address - &spl_token::id(), // Token program + &test_context.payer.pubkey(), // Funding account + &owner.pubkey(), // Wallet address + mint, // Mint address + &spl_token::id(), // Token program ); // Create and process transaction let tx = Transaction::new_signed_with_payer( &[create_ata_ix], - Some(&test_ctx.payer.pubkey()), - &[&test_ctx.payer], - test_ctx.last_blockhash, + Some(&test_context.payer.pubkey()), + &[&test_context.payer], + test_context.last_blockhash, ); - test_ctx.banks_client.process_transaction(tx).await.unwrap(); + test_context + .banks_client + .process_transaction(tx) + .await + .unwrap(); // Get the account - test_ctx + test_context .banks_client .get_account(token_account_address) .await @@ -154,3 +158,9 @@ pub enum SplTokenEnum { Usdc, Usdt, } + +impl Default for SplTokenEnum { + fn default() -> Self { + Self::Usdc + } +} diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index 0600b161e..580682c36 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -227,12 +227,12 @@ impl PayloadDeserialized { } } - // pub fn get_fast_transfer(&self) -> Option { - // match self { - // Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), - // _ => None, - // } - // } + pub fn get_fast_transfer(&self) -> Option { + match self { + Self::FastTransfer(fast_transfer) => Some(fast_transfer.clone()), + _ => None, + } + } } /// A struct representing a test VAA (may be posted or not) @@ -263,6 +263,10 @@ impl TestVaa { pub fn get_vaa_data(&self) -> &PostedVaaData { &self.vaa_data } + + pub fn get_payload_deserialized(&self) -> Option<&PayloadDeserialized> { + self.payload_deserialized.as_ref() + } } #[derive(Clone)] @@ -500,6 +504,24 @@ impl TestVaaPair { pub fn is_posted(&self) -> bool { self.deposit_vaa.is_posted && self.fast_transfer_vaa.is_posted } + + pub fn get_fast_transfer_vaa_expiration_time(&self) -> u32 { + let two_hours_in_seconds = 7200; + let vaa_time = self.fast_transfer_vaa.vaa_data.vaa_time; + let expiration = vaa_time.saturating_add(two_hours_in_seconds); + let deadline = self + .fast_transfer_vaa + .get_payload_deserialized() + .unwrap() + .get_fast_transfer() + .unwrap() + .deadline; + if expiration < deadline || deadline == 0 { + expiration + } else { + deadline + } + } } /// Creates a deposit message diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 8993feef1..d57b0df66 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -59,11 +59,20 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { msg!("Refund recipient (account #2) is not a signer"); return Err(ProgramError::InvalidAccountData.into()); } - + let fast_market_order_deserialized = + FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; // Check that the fast_market_order is owned by the close_account_refund_recipient - if fast_market_order.owner != &close_account_refund_recipient.key() { - msg!("Fast market order is not owned by the close account refund recipient"); - return Err(ErrorCode::ConstraintOwner.into()); + if fast_market_order_deserialized.close_account_refund_recipient + != close_account_refund_recipient.key().as_ref() + { + return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( + |e: Error| { + e.with_pubkeys(( + Pubkey::from(fast_market_order_deserialized.close_account_refund_recipient), + close_account_refund_recipient.key(), + )) + }, + ); } let fast_market_order_data = diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index c9d724173..b07570e2f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -233,13 +233,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .map_err(|e: Error| e.with_account_name("custodian")); }; - // Check custodian is not paused - let checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; - if checked_custodian.paused { - msg!("Custodian is paused"); - return Err(ErrorCode::ConstraintRaw.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - }; + // Check custodian deserialises into a checked custodian account + let _checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; let fast_market_order_digest = fast_market_order_zero_copy.digest(); // Check fast market order seeds diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 46a989afd..5636c6408 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -240,7 +240,7 @@ pub fn place_initial_offer_cctp_shim( let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; if checked_custodian.paused { msg!("Custodian is paused"); - return Err(ErrorCode::ConstraintRaw.into()) + return Err(MatchingEngineError::Paused.into()) .map_err(|e: Error| e.with_account_name("custodian")); } // Check auction_config owner From 17eb8d28037e263b2eeb60d854adef948af1d2d9 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 18 Apr 2025 17:47:31 +0100 Subject: [PATCH 059/112] execute order testing checks improved, fix to prepare order instruction, all tests pass --- .../tests/shimful/shims_execute_order.rs | 31 +- .../shimful/shims_prepare_order_response.rs | 189 ++--- .../tests/shimless/execute_order.rs | 33 +- .../tests/shimless/prepare_order_response.rs | 91 +- .../create_and_close_fast_market_order.rs | 28 +- .../tests/test_scenarios/execute_order.rs | 796 ++++++++++++++++-- .../test_scenarios/initialise_and_misc.rs | 8 +- .../tests/test_scenarios/make_offer.rs | 119 ++- .../tests/test_scenarios/prepare_order.rs | 260 +++++- .../tests/test_scenarios/settle_auction.rs | 48 +- .../tests/testing_engine/config.rs | 85 +- .../tests/testing_engine/engine.rs | 163 ++-- .../tests/testing_engine/setup.rs | 203 +++-- .../tests/testing_engine/state.rs | 4 +- .../tests/utils/auction.rs | 100 ++- .../tests/utils/cctp_message.rs | 68 +- .../tests/utils/public_keys.rs | 20 +- .../tests/utils/vaa.rs | 60 +- .../src/fallback/processor/execute_order.rs | 15 +- .../processor/prepare_order_response.rs | 141 ++-- .../src/state/fast_market_order.rs | 3 +- .../src/state/prepared_order_response.rs | 1 - 22 files changed, 1860 insertions(+), 606 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 6d99b7961..030a7ef6c 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -42,14 +42,17 @@ impl ExecuteOrderFallbackAccounts { current_state: &TestingEngineState, payer_signer: &Pubkey, fixture_accounts: &utils::account_fixtures::FixtureAccounts, + override_fast_market_order_address: Option, ) -> Self { let transfer_direction = current_state.base().transfer_direction; let auction_accounts = current_state.auction_accounts().unwrap(); let active_auction_state = current_state.auction_state().get_active_auction().unwrap(); - let fast_market_order_address = current_state - .fast_market_order() - .unwrap() - .fast_market_order_address; + let fast_market_order_address = override_fast_market_order_address.unwrap_or_else(|| { + current_state + .fast_market_order() + .unwrap() + .fast_market_order_address + }); let remote_token_messenger = match transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.arbitrum_remote_token_messenger @@ -95,7 +98,7 @@ pub struct ExecuteOrderFallbackFixtureAccounts { pub executor_token: Pubkey, } -pub async fn execute_order_fallback( +pub async fn execute_order_shimful( testing_context: &TestingContext, test_context: &mut ProgramTestContext, config: &ExecuteOrderInstructionConfig, @@ -131,7 +134,11 @@ pub async fn execute_order_fallback( .get_new_latest_blockhash(test_context) .await .unwrap(); - crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; + let slots_to_fast_forward = config.fast_forward_slots; + if slots_to_fast_forward > 0 { + crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) + .await; + } let transaction = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), @@ -262,7 +269,7 @@ pub fn create_execute_order_shim_accounts<'ix>( } } -pub async fn execute_order_fallback_test( +pub async fn execute_order_shimful_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, @@ -276,9 +283,13 @@ pub async fn execute_order_fallback_test( .payer_signer .clone() .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let execute_order_fallback_accounts = - ExecuteOrderFallbackAccounts::new(current_state, &payer_signer.pubkey(), &fixture_accounts); - execute_order_fallback( + let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( + current_state, + &payer_signer.pubkey(), + &fixture_accounts, + config.fast_market_order_address, + ); + execute_order_shimful( testing_context, test_context, config, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index 46660288d..5ecb328e1 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -1,4 +1,6 @@ -use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::{ + ExpectedError, InstructionConfig, PrepareOrderResponseInstructionConfig, +}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::testing_engine::state::TestingEngineState; @@ -9,10 +11,9 @@ use anchor_spl::token::spl_token; use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; -use common::wormhole_cctp_solana::messages::Deposit; use common::wormhole_cctp_solana::utils::CctpMessage; use matching_engine::fallback::prepare_order_response::{ - FinalizedVaaMessage, PrepareOrderResponseCctpShim as PrepareOrderResponseCctpShimIx, + FinalizedVaaMessageArgs, PrepareOrderResponseCctpShim as PrepareOrderResponseCctpShimIx, PrepareOrderResponseCctpShimAccounts, PrepareOrderResponseCctpShimData, }; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; @@ -22,7 +23,6 @@ use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; use std::rc::Rc; -use utils::account_fixtures::FixtureAccounts; use utils::cctp_message::{CctpMessageDecoded, UsedNonces}; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; @@ -53,19 +53,36 @@ pub struct PrepareOrderResponseShimAccountsFixture { } impl PrepareOrderResponseShimAccountsFixture { - #[allow(clippy::too_many_arguments)] // TODO: fix this (10/7) pub fn new( + testing_context: &TestingContext, + config: &PrepareOrderResponseInstructionConfig, + current_state: &TestingEngineState, signer: &Pubkey, - fixture_accounts: &FixtureAccounts, - custodian_address: &Pubkey, - fast_market_order_address: &Pubkey, - from_router_endpoint: &Pubkey, - to_router_endpoint: &Pubkey, - usdc_mint_address: &Pubkey, cctp_message_decoded: &CctpMessageDecoded, guardian_signature_info: &GuardianSignatureInfo, - transfer_direction: &TransferDirection, ) -> Self { + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let to_endpoint = auction_accounts.to_router_endpoint; + let from_endpoint = auction_accounts.from_router_endpoint; + let fast_market_order = current_state + .fast_market_order() + .expect("could not find fast market order") + .fast_market_order_address; + let base_fee_token = config + .actor_enum + .get_actor(&testing_context.testing_actors) + .token_account_address(&config.token_enum) + .unwrap(); + let fixture_accounts = testing_context + .fixture_accounts + .clone() + .expect("Fixture accounts not found"); + let custodian = current_state + .custodian_address() + .expect("Custodian address not found"); let cctp_message_transmitter_event_authority = Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &MESSAGE_TRANSMITTER_PROGRAM_ID) .0; @@ -86,7 +103,7 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_message_decoded.source_domain, cctp_message_decoded.nonce, ); - let cctp_remote_token_messenger = match transfer_direction { + let cctp_remote_token_messenger = match testing_context.transfer_direction { TransferDirection::FromEthereumToArbitrum => { fixture_accounts.ethereum_remote_token_messenger } @@ -97,12 +114,12 @@ impl PrepareOrderResponseShimAccountsFixture { }; Self { signer: *signer, - custodian: *custodian_address, - fast_market_order: *fast_market_order_address, - from_endpoint: *from_router_endpoint, - to_endpoint: *to_router_endpoint, - base_fee_token: *usdc_mint_address, // Change this to the solver's address? - usdc: *usdc_mint_address, + custodian, + fast_market_order, + from_endpoint, + to_endpoint, + base_fee_token, + usdc: usdc_mint_address, cctp_mint_recipient: CCTP_MINT_RECIPIENT, cctp_message_transmitter_authority, cctp_message_transmitter_config: fixture_accounts.message_transmitter_config, @@ -126,39 +143,29 @@ impl PrepareOrderResponseShimAccountsFixture { pub struct PrepareOrderResponseShimDataFixture { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, - pub finalized_vaa_message_sequence: u64, - pub finalized_vaa_message_timestamp: u32, - pub finalized_vaa_message_emitter_chain: u16, - pub finalized_vaa_message_emitter_address: [u8; 32], - pub finalized_vaa_message_base_fee: u64, - pub vaa_payload: Vec, - pub deposit_payload: Vec, + pub finalized_vaa_message_args: FinalizedVaaMessageArgs, pub fast_market_order: FastMarketOrderState, - pub guardian_set_bump: u8, } +// Helper struct for creating the data for the prepare order response instruction impl PrepareOrderResponseShimDataFixture { pub fn new( encoded_cctp_message: Vec, cctp_attestation: Vec, - deposit_vaa_data: &utils::vaa::PostedVaaData, - deposit: &Deposit, - deposit_base_fee: u64, + consistency_level: u8, + base_fee: u64, fast_market_order: &FastMarketOrderState, guardian_set_bump: u8, ) -> Self { Self { encoded_cctp_message, cctp_attestation, - finalized_vaa_message_sequence: deposit_vaa_data.sequence, - finalized_vaa_message_timestamp: deposit_vaa_data.vaa_time, - finalized_vaa_message_emitter_chain: deposit_vaa_data.emitter_chain, - finalized_vaa_message_emitter_address: deposit_vaa_data.emitter_address, - finalized_vaa_message_base_fee: deposit_base_fee, - vaa_payload: deposit_vaa_data.payload.to_vec(), - deposit_payload: deposit.payload.to_vec(), + finalized_vaa_message_args: FinalizedVaaMessageArgs { + consistency_level, + base_fee, + guardian_set_bump, + }, fast_market_order: *fast_market_order, - guardian_set_bump, } } pub fn decode_cctp_message(&self) -> CctpMessageDecoded { @@ -170,15 +177,16 @@ impl PrepareOrderResponseShimDataFixture { } } +/// Executes the instruction that prepares the order response for the CCTP shim pub async fn prepare_order_response_cctp_shim( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Rc, accounts: PrepareOrderResponseShimAccountsFixture, data: PrepareOrderResponseShimDataFixture, - matching_engine_program_id: &Pubkey, expected_error: Option<&ExpectedError>, ) -> Option { + let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let fast_market_order_digest = data.fast_market_order.digest(); let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, @@ -229,16 +237,11 @@ pub async fn prepare_order_response_cctp_shim( system_program: &solana_program::system_program::ID, }; - let finalized_vaa_message = FinalizedVaaMessage { - base_fee: data.finalized_vaa_message_base_fee, - vaa_payload: data.vaa_payload, - deposit_payload: data.deposit_payload, - guardian_set_bump: data.guardian_set_bump, - }; + let finalized_vaa_message_args = data.finalized_vaa_message_args; let data = PrepareOrderResponseCctpShimData { encoded_cctp_message: data.encoded_cctp_message, cctp_attestation: data.cctp_attestation, - finalized_vaa_message, + finalized_vaa_message_args, }; let prepare_order_response_cctp_shim_ix = PrepareOrderResponseCctpShimIx { @@ -272,69 +275,55 @@ pub async fn prepare_order_response_cctp_shim( } } -#[allow(clippy::too_many_arguments)] pub async fn prepare_order_response_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - payer_signer: &Rc, - deposit_vaa_data: &utils::vaa::PostedVaaData, - testing_engine_state: &TestingEngineState, - to_endpoint_address: &Pubkey, - from_endpoint_address: &Pubkey, - deposit: &Deposit, - expected_error: Option<&ExpectedError>, + config: &PrepareOrderResponseInstructionConfig, + current_state: &TestingEngineState, ) -> Option { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let deposit_vaa = current_state + .get_test_vaa_pair(config.vaa_index) + .deposit_vaa + .clone(); + let deposit_vaa_data = deposit_vaa.get_vaa_data(); + let deposit = deposit_vaa + .payload_deserialized + .clone() + .unwrap() + .get_deposit() + .unwrap(); let core_bridge_program_id = &testing_context.get_wormhole_program_id(); - let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); - let usdc_mint_address = &testing_context.get_usdc_mint_address(); - let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); - let fixture_accounts = testing_context - .fixture_accounts - .clone() - .expect("Fixture accounts not found"); + let finalized_vaa_data = current_state + .get_test_vaa_pair(config.vaa_index) + .get_finalized_vaa_data() + .clone(); let guardian_signature_info = super::verify_shim::create_guardian_signatures( testing_context, test_context, - payer_signer, - deposit_vaa_data, + &payer_signer, + &finalized_vaa_data, core_bridge_program_id, None, ) .await .unwrap(); - let source_remote_token_messenger = match testing_context.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; - let cctp_nonce = deposit.cctp_nonce; - - let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; - let fast_market_order_state = testing_engine_state + let fast_market_order_state = current_state .fast_market_order() .expect("could not find fast market order") .fast_market_order; - let custodian_address = testing_engine_state - .custodian_address() - .expect("Custodian address not found"); - // TODO: Make checks to see if fast market order sender matches cctp message sender ... + let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( + testing_context, test_context, - source_remote_token_messenger.domain, - cctp_nonce, - deposit.amount, - &message_transmitter_config_pubkey, - &(&source_remote_token_messenger).into(), - cctp_mint_recipient, - &custodian_address, + current_state, + config.vaa_index, ) .await .unwrap(); @@ -342,41 +331,31 @@ pub async fn prepare_order_response_test( .verify_cctp_message(&fast_market_order_state) .unwrap(); - let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(deposit); + let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new( cctp_token_burn_message.encoded_cctp_burn_message, cctp_token_burn_message.cctp_attestation, - deposit_vaa_data, - deposit, + deposit_vaa_data.consistency_level, deposit_base_fee, &fast_market_order_state, guardian_signature_info.guardian_set_bump, ); - let fast_market_order_address = testing_engine_state - .fast_market_order() - .expect("could not find fast market order") - .fast_market_order_address; let cctp_message_decoded = prepare_order_response_cctp_shim_data.decode_cctp_message(); let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new( + testing_context, + config, + current_state, &payer_signer.pubkey(), - &fixture_accounts, - &custodian_address, - &fast_market_order_address, - from_endpoint_address, - to_endpoint_address, - usdc_mint_address, &cctp_message_decoded, &guardian_signature_info, - &testing_context.transfer_direction, ); super::shims_prepare_order_response::prepare_order_response_cctp_shim( testing_context, test_context, - payer_signer, + &payer_signer, prepare_order_response_cctp_shim_accounts, prepare_order_response_cctp_shim_data, - matching_engine_program_id, - expected_error, + config.expected_error(), ) .await } diff --git a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs index 191eb7d86..7e01382cd 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::{ExecuteOrderInstructionConfig, ExpectedError}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::utils::account_fixtures::FixtureAccounts; use crate::utils::auction::{AuctionAccounts, AuctionState}; @@ -32,6 +32,7 @@ pub fn create_execute_order_shimless_accounts( auction_accounts: &AuctionAccounts, payer_signer: &Rc, auction_state: &AuctionState, + executor_token: Pubkey, ) -> ExecuteOrderShimlessAccounts { let active_auction_state = auction_state.get_active_auction().unwrap(); let active_auction_address = active_auction_state.auction_address; @@ -74,7 +75,7 @@ pub fn create_execute_order_shimless_accounts( let execute_order = matching_engine::accounts::ExecuteOrder { fast_vaa, active_auction, - executor_token: active_auction_state.best_offer.offer_token, // TODO: Is this correct? + executor_token, initial_participant: active_auction_state.initial_offer.participant, initial_offer_token: active_auction_state.initial_offer.offer_token, }; @@ -155,12 +156,31 @@ pub fn create_execute_order_shimless_accounts( pub async fn execute_order_shimless_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, + config: &ExecuteOrderInstructionConfig, auction_accounts: &AuctionAccounts, auction_state: &AuctionState, - payer_signer: &Rc, expected_error: Option<&ExpectedError>, ) -> Option { - crate::testing_engine::engine::fast_forward_slots(test_context, 3).await; + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let slots_to_fast_forward = config.fast_forward_slots; + if slots_to_fast_forward > 0 { + crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) + .await; + } + let executor_token = config + .actor_enum + .get_actor(&testing_context.testing_actors) + .token_account_address(&config.token_enum) + .unwrap_or_else(|| { + auction_state + .get_active_auction() + .unwrap() + .best_offer + .offer_token + }); let fixture_accounts = testing_context .get_fixture_accounts() .expect("Fixture accounts not found"); @@ -169,8 +189,9 @@ pub async fn execute_order_shimless_test( testing_context, &fixture_accounts, auction_accounts, - payer_signer, + &payer_signer, auction_state, + executor_token, ); let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); let execute_order_ix = Instruction { @@ -181,7 +202,7 @@ pub async fn execute_order_shimless_test( let tx = Transaction::new_signed_with_payer( &[execute_order_ix], Some(&payer_signer.pubkey()), - &[payer_signer], + &[&payer_signer], testing_context .get_new_latest_blockhash(test_context) .await diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 9d0debcba..10367dea6 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -1,5 +1,4 @@ -use crate::testing_engine::config::ExpectedError; -use crate::testing_engine::config::ExpectedLog; +use crate::testing_engine::config::{InstructionConfig, PrepareOrderResponseInstructionConfig}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; use crate::testing_engine::state::TestingEngineState; use crate::utils; @@ -20,9 +19,8 @@ use matching_engine::state::PreparedOrderResponse; use matching_engine::CctpMessageArgs; use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; -use solana_sdk::signature::{Keypair, Signer}; +use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; -use std::rc::Rc; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; pub struct PrepareOrderResponseFixture { @@ -39,29 +37,31 @@ pub struct PrepareOrderResponseFixture { /// /// * `testing_context` - The testing context /// * `test_context` - The test context -/// * `payer_signer` - The payer signer -/// * `testing_engine_state` - The testing engine state -/// * `to_endpoint_address` - The to endpoint address -/// * `from_endpoint_address` - The from endpoint address +/// * `config` - The prepare order response instruction config +/// * `current_state` - The current state /// * `base_fee_token_address` - The base fee token address -/// * `expected_error` - The expected error -/// * `expected_log_messages` - The expected log messages /// /// # Returns /// /// The prepared order response fixture if successful, otherwise None -#[allow(clippy::too_many_arguments)] pub async fn prepare_order_response( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - payer_signer: &Rc, - testing_engine_state: &TestingEngineState, - to_endpoint_address: &Pubkey, - from_endpoint_address: &Pubkey, + config: &PrepareOrderResponseInstructionConfig, + current_state: &TestingEngineState, base_fee_token_address: &Pubkey, - expected_error: Option<&ExpectedError>, - expected_log_messages: Option<&Vec>, ) -> Option { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let to_endpoint_address = &auction_accounts.to_router_endpoint; + let from_endpoint_address = &auction_accounts.from_router_endpoint; + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let expected_error = config.expected_error(); + let expected_log_messages = config.expected_log_messages(); let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); @@ -70,42 +70,25 @@ pub async fn prepare_order_response( .clone() .expect("Fixture accounts not found"); - let source_remote_token_messenger = match testing_context.transfer_direction { - TransferDirection::FromEthereumToArbitrum => { - utils::router::get_remote_token_messenger( - test_context, - fixture_accounts.ethereum_remote_token_messenger, - ) - .await - } - _ => panic!("Unsupported transfer direction"), - }; - - let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; - let custodian_address = testing_engine_state - .custodian_address() - .expect("Custodian address not found"); - let first_vaa_pair = testing_engine_state.get_first_test_vaa_pair(); - let posted_fast_transfer_vaa = first_vaa_pair.clone().fast_transfer_vaa; + let vaa_pair = current_state.get_test_vaa_pair(config.vaa_index); + let posted_fast_transfer_vaa = vaa_pair.clone().fast_transfer_vaa; let posted_fast_transfer_vaa_address = posted_fast_transfer_vaa.vaa_pubkey; - let deposit = first_vaa_pair + let cctp_nonce = vaa_pair .deposit_vaa - .clone() - .payload_deserialized + .get_payload_deserialized() .unwrap() .get_deposit() - .unwrap(); - let cctp_nonce = deposit.cctp_nonce; + .unwrap() + .cctp_nonce; + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); // TODO: Make checks to see if fast market order sender matches cctp message sender ... let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( + testing_context, test_context, - source_remote_token_messenger.domain, - cctp_nonce, - deposit.amount, - &message_transmitter_config_pubkey, - &(&source_remote_token_messenger).into(), - cctp_mint_recipient, - &custodian_address, + current_state, + config.vaa_index, ) .await .unwrap(); @@ -127,7 +110,7 @@ pub async fn prepare_order_response( }, }; let finalized_vaa = LiquidityLayerVaa { - vaa: first_vaa_pair.deposit_vaa.vaa_pubkey, + vaa: vaa_pair.deposit_vaa.vaa_pubkey, }; let fast_transfer_digest = posted_fast_transfer_vaa.get_vaa_data().digest(); let prepared_order_response_seeds = [ @@ -146,8 +129,13 @@ pub async fn prepare_order_response( let usdc = Usdc { mint: *usdc_mint_address, }; + + let remote_token_messenger = testing_context + .get_remote_token_messenger(test_context) + .await; + let (used_nonces_pda, _used_nonces_bump) = - UsedNonces::address(source_remote_token_messenger.domain, cctp_nonce); + UsedNonces::address(remote_token_messenger.domain, cctp_nonce); let cctp_message_transmitter_authority = Pubkey::find_program_address( &[ b"message_transmitter_authority", @@ -173,6 +161,11 @@ pub async fn prepare_order_response( } _ => panic!("Unsupported transfer direction"), }; + let fixture_accounts = testing_context + .fixture_accounts + .clone() + .expect("Fixture accounts not found"); + let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; let cctp = CctpReceiveMessage { mint_recipient: cctp_mint_recipient, message_transmitter_authority: cctp_message_transmitter_authority, @@ -220,7 +213,7 @@ pub async fn prepare_order_response( let transaction = Transaction::new_signed_with_payer( &[instruction], Some(&payer_signer.pubkey()), - &[payer_signer], + &[&payer_signer], testing_context .get_new_latest_blockhash(test_context) .await diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs index ce8098014..75427a5a0 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs @@ -56,10 +56,10 @@ use utils::vaa::VaaArgs; /// Test that the create fast market order account works correctly for the fallback instruction #[tokio::test] pub async fn test_initialise_fast_market_order_fallback() { - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, @@ -83,10 +83,10 @@ pub async fn test_initialise_fast_market_order_fallback() { /// Test that the close fast market order account works correctly for the fallback instruction #[tokio::test] pub async fn test_close_fast_market_order_fallback() { - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, @@ -111,10 +111,10 @@ pub async fn test_close_fast_market_order_fallback() { /// Test that the close fast market order account works correctly for the fallback instruction #[tokio::test] pub async fn test_close_fast_market_order_fallback_with_custom_refund_recipient() { - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, @@ -179,10 +179,10 @@ pub async fn test_close_fast_market_order_fallback_with_custom_refund_recipient( #[tokio::test] pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialise_it() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, @@ -225,10 +225,10 @@ pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_in #[tokio::test] pub async fn test_fast_market_order_cannot_be_closed_twice() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -273,10 +273,10 @@ a8" "" a8P_____88 88P' `"8a I8[ "" a8" "8a 88P' "Y8 a8P_____88 a8 #[tokio::test] pub async fn test_fast_market_order_can_be_opened_after_being_closed_by_the_same_solver() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -306,10 +306,10 @@ pub async fn test_fast_market_order_can_be_opened_after_being_closed_by_the_same pub async fn test_multiple_fast_market_orders_can_be_opened_and_closed_by_different_solvers_in_arbitrary_order( ) { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index ebd36dabe..3ece5c228 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -1,9 +1,3 @@ -// TODO: -// Shouldn't be able to improve offer after executing order -// Try within a grace period and after grace period to see if math checks out -// After grace period, try to execute order with different account to best offer to see if math checks out -// Check that the initial offer token being closed doesnt stop the execute order - //! # Execute order instruction testing //! //! This module contains tests for the execute order instruction. @@ -15,19 +9,21 @@ //! - `test_execute_order_shimless` - Test that the execute order shimless instruction works correctly //! +use std::collections::HashSet; + use crate::test_scenarios::make_offer::place_initial_offer_shimless; use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; -use crate::testing_engine::engine::{ - fast_forward_slots, ExecutionChain, ExecutionTrigger, VerificationTrigger, -}; +use crate::testing_engine::engine::{ExecutionChain, ExecutionTrigger, VerificationTrigger}; use crate::testing_engine::state::TestingEngineState; -use crate::utils; +use crate::utils::public_keys::ChainAddress; use crate::utils::token_account::SplTokenEnum; +use crate::utils::{self, Chain}; use anchor_lang::error::ErrorCode; +use matching_engine::error::MatchingEngineError; use solana_program_test::{tokio, ProgramTestContext}; use testing_engine::config::*; use testing_engine::engine::{InstructionTrigger, TestingEngine}; @@ -140,10 +136,10 @@ pub async fn test_execute_order_shimless_after_placing_initial_offer_with_shim() let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shim( PlaceInitialOfferInstructionConfig::default(), - Some(VaaArgs { + Some(vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }), + }]), transfer_direction, )) .await; @@ -167,40 +163,154 @@ pub async fn test_execute_order_shimless_after_placing_initial_offer_with_shim() #[tokio::test] pub async fn test_execute_order_shim_after_grace_period() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { - post_vaa: true, - ..VaaArgs::default() + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after grace period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + ..ExecuteOrderInstructionConfig::default() }; - let (testing_context, mut test_context) = setup_environment( - ShimMode::VerifyAndPostSignature, - transfer_direction, - Some(vaa_args), - ) - .await; - let testing_engine = TestingEngine::new(testing_context).await; - let instruction_vec = vec![ - InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), - InstructionTrigger::CreateCctpRouterEndpoints( - CreateCctpRouterEndpointsInstructionConfig::default(), - ), - InstructionTrigger::InitializeFastMarketOrderShim( - InitializeFastMarketOrderShimInstructionConfig::default(), - ), - InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), - ]; - let place_initial_offer_state = testing_engine - .execute(&mut test_context, instruction_vec, None) + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim(execute_order_config)]; + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test executing order shimless after grace period +#[tokio::test] +pub async fn test_execute_order_shimless_after_grace_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after grace period .await; - fast_forward_slots(&mut test_context, 100).await; let previous_state_balances = testing_engine .testing_context .get_balances(&mut test_context) .await; - let execute_order_config = ExecuteOrderInstructionConfig::default(); + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + ..ExecuteOrderInstructionConfig::default() + }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let mut execution_chain = ExecutionChain::from(instruction_triggers); + execution_chain.push(ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))); + let _ = testing_engine + .execute( + &mut test_context, + execution_chain, + Some(place_initial_offer_state), + ) + .await; +} + +/// Test executing order shim after grace period with different executor +#[tokio::test] +pub async fn test_execute_order_shim_after_grace_period_with_different_executor() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after grace period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + actor_enum: TestingActorEnum::Solver(1), + ..ExecuteOrderInstructionConfig::default() + }; let executor_actor = execute_order_config .actor_enum .get_actor(&testing_engine.testing_context.testing_actors); let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim(execute_order_config)]; + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; let execute_order_state = testing_engine .execute( &mut test_context, @@ -208,34 +318,156 @@ pub async fn test_execute_order_shim_after_grace_period() { Some(place_initial_offer_state), ) .await; - let active_auction_state = execute_order_state + + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test executing order shimless after grace period with different executor +#[tokio::test] +pub async fn test_execute_order_shimless_after_grace_period_with_different_executor() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after grace period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + actor_enum: TestingActorEnum::Solver(1), + ..ExecuteOrderInstructionConfig::default() + }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let custodian_token_previous_balance = place_initial_offer_state .auction_state() .get_active_auction() - .unwrap(); - let executor_enum = execute_order_state.execute_order_actor().unwrap(); - let best_offer_enum = execute_order_state.best_offer_actor().unwrap(); - let initial_offer_enum = execute_order_state.initial_offer_placed_actor().unwrap(); - let execute_order_actor_enums = ExecuteOrderActorEnums { - executor: executor_enum, - best_offer: best_offer_enum, - initial_offer: initial_offer_enum, + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test executing order shim after grace period with initial offer token closed +#[tokio::test] +pub async fn test_execute_order_shim_after_grace_period_with_initial_offer_token_closed() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after grace period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + actor_enum: TestingActorEnum::Solver(1), + ..ExecuteOrderInstructionConfig::default() }; - let verification_trigger = VerificationTrigger::VerifyBalances(VerifyBalancesConfig { - previous_state_balances, - balance_changes: BalanceChanges::execute_order_changes( - &mut test_context, - &execute_order_state, - executor_actor, - execute_order_actor_enums, - active_auction_state, - SplTokenEnum::Usdc, - ) - .await, - }); + testing_engine + .close_token_account( + &mut test_context, + &TestingActorEnum::Solver(0), + &SplTokenEnum::Usdc, + ) + .await; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim(execute_order_config)]; + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: Some(HashSet::from([TestingActorEnum::Solver(0)])), + })); let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( verification_trigger, ))]); - let _ = testing_engine + testing_engine .execute( &mut test_context, execution_chain, @@ -244,9 +476,9 @@ pub async fn test_execute_order_shim_after_grace_period() { .await; } -/// Test executing order shimless after grace period +/// Test executing order shim after grace period with initial offer token closed #[tokio::test] -pub async fn test_execute_order_shimless_after_grace_period() { +pub async fn test_execute_order_shimless_after_grace_period_with_initial_offer_token_closed() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shimless( @@ -255,54 +487,331 @@ pub async fn test_execute_order_shimless_after_grace_period() { transfer_direction, )) .await; - fast_forward_slots(&mut test_context, 100).await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) // 1 slots after grace period + .await; + // Close the token account of the initial offer + testing_engine + .close_token_account( + &mut test_context, + &TestingActorEnum::Solver(0), + &SplTokenEnum::Usdc, + ) + .await; let previous_state_balances = testing_engine .testing_context .get_balances(&mut test_context) .await; - let execute_order_config = ExecuteOrderInstructionConfig::default(); + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + actor_enum: TestingActorEnum::Solver(1), + ..ExecuteOrderInstructionConfig::default() + }; let executor_actor = execute_order_config .actor_enum .get_actor(&testing_engine.testing_context.testing_actors); + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( execute_order_config, )]; - let active_auction_state = place_initial_offer_state + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: Some(HashSet::from([TestingActorEnum::Solver(0)])), + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test execute order shim after auction passed penalty period +#[tokio::test] +pub async fn test_execute_order_shim_after_auction_passed_penalty_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_penalty_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after penalty period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let custodian_token_previous_balance = place_initial_offer_state .auction_state() .get_active_auction() - .unwrap(); - let executor_enum = TestingActorEnum::Solver(0); - let best_offer_enum = TestingActorEnum::Solver(0); - let initial_offer_enum = TestingActorEnum::Solver(0); - let execute_order_actor_enums = ExecuteOrderActorEnums { - executor: executor_enum, - best_offer: best_offer_enum, - initial_offer: initial_offer_enum, + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + ..ExecuteOrderInstructionConfig::default() }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim(execute_order_config)]; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} - let verification_trigger = VerificationTrigger::VerifyBalances(VerifyBalancesConfig { - previous_state_balances, - balance_changes: BalanceChanges::execute_order_changes( +/// Test execute order shimless after auction passed penalty period +#[tokio::test] +pub async fn test_execute_order_shimless_after_auction_passed_penalty_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_penalty_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after penalty period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + ..ExecuteOrderInstructionConfig::default() + }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let execute_order_state = testing_engine + .execute( &mut test_context, - &place_initial_offer_state, - executor_actor, - execute_order_actor_enums, - active_auction_state, - SplTokenEnum::Usdc, + instruction_triggers, + Some(place_initial_offer_state), ) - .await, - }); - let mut execution_chain = ExecutionChain::from(instruction_triggers); - execution_chain.push(ExecutionTrigger::Verification(Box::new( + .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( verification_trigger, - ))); - let _ = testing_engine + ))]); + testing_engine .execute( &mut test_context, execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test execute order shimless after auction passed penalty period, and executor != best offer +#[tokio::test] +pub async fn test_execute_order_shimless_after_auction_passed_penalty_period_and_executor_not_best_offer( +) { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_penalty_period(&mut test_context, &place_initial_offer_state, 1) // 1 slot after penalty period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + actor_enum: TestingActorEnum::Solver(1), + ..ExecuteOrderInstructionConfig::default() + }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let custodian_token_previous_balance = place_initial_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, Some(place_initial_offer_state), ) .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; +} + +/// Test execute order shimless initial offer token != best offer token +#[tokio::test] +pub async fn test_execute_order_shimless_after_penalty_period_initial_offer_token_not_best_offer_token( +) { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::ImproveOfferShimless( + ImproveOfferInstructionConfig { + actor: TestingActorEnum::Solver(1), + ..Default::default() + }, + )]; + let improve_offer_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; + testing_engine + .make_auction_passed_penalty_period(&mut test_context, &improve_offer_state, 1) // 1 slot after penalty period + .await; + let previous_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let execute_order_config = ExecuteOrderInstructionConfig { + actor_enum: TestingActorEnum::Solver(0), + ..ExecuteOrderInstructionConfig::default() + }; + let executor_actor = execute_order_config + .actor_enum + .get_actor(&testing_engine.testing_context.testing_actors); + let custodian_token_previous_balance = improve_offer_state + .auction_state() + .get_active_auction() + .unwrap() + .get_auction_custody_token_balance(&mut test_context) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShimless( + execute_order_config, + )]; + let execute_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(improve_offer_state), + ) + .await; + let verification_trigger = + VerificationTrigger::VerifyBalances(Box::new(VerifyBalancesConfig { + previous_state_balances, + balance_changes_config: BalanceChangesConfig { + actor: executor_actor, + spl_token_enum: SplTokenEnum::Usdc, + custodian_token_previous_balance, + }, + closed_token_account_enums: None, + })); + let execution_chain = ExecutionChain::new(vec![ExecutionTrigger::Verification(Box::new( + verification_trigger, + ))]); + testing_engine + .execute( + &mut test_context, + execution_chain, + Some(execute_order_state), + ) + .await; } /// Test executing order shim after custodian is paused after initial offer @@ -401,10 +910,10 @@ pub async fn test_execute_order_shimless_after_custodian_is_paused_after_initial #[tokio::test] pub async fn test_execute_order_shim_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -482,7 +991,7 @@ pub async fn test_execute_order_shim_after_close_fast_market_order_fails() { /// Cannot improve offer after executing order #[tokio::test] -pub async fn test_cannot_improve_offer_after_executing_order() { +pub async fn test_execute_order_cannot_improve_offer_after_executing_order() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; let (place_initial_offer_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shim( @@ -511,10 +1020,117 @@ pub async fn test_cannot_improve_offer_after_executing_order() { .await; } +/// Cannot execute order with incorrect emitter chain +#[tokio::test] +pub async fn test_execute_order_shim_emitter_chain_mismatch() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = vec![ + VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }, + VaaArgs { + post_vaa: false, + override_emitter_chain_and_address: Some(ChainAddress::from_registered_token_router( + Chain::Arbitrum, + )), + ..VaaArgs::default() + }, + ]; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let initialise_first_fast_market_order_instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + ]; + let initialise_first_fast_market_order_state = testing_engine + .execute( + &mut test_context, + initialise_first_fast_market_order_instruction_triggers, + None, + ) + .await; + let initialise_second_fast_market_order_instruction_triggers = vec![ + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 1, + vaa_index: 1, + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + ]; + let initialise_second_fast_market_order_state = testing_engine + .execute( + &mut test_context, + initialise_second_fast_market_order_instruction_triggers, + Some(initialise_first_fast_market_order_state), + ) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( + ExecuteOrderInstructionConfig { + vaa_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::VaaMismatch), + error_string: "AccountNotInitialized".to_string(), + }), + ..ExecuteOrderInstructionConfig::default() + }, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initialise_second_fast_market_order_state), + ) + .await; +} + +/// Cannot execute order shim before auction duration is over +#[tokio::test] +pub async fn test_execute_order_shim_before_auction_duration_is_over() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( + ExecuteOrderInstructionConfig { + fast_forward_slots: 0, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::AuctionPeriodNotExpired), + error_string: "AuctionPeriodNotExpired".to_string(), + }), + ..ExecuteOrderInstructionConfig::default() + }, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + /* Helper code */ - pub enum ShimExecutionMode { Shim, Shimless, @@ -523,7 +1139,7 @@ pub enum ShimExecutionMode { pub async fn execute_order_helper( config: ExecuteOrderInstructionConfig, shim_execution_mode: ShimExecutionMode, - vaa_args: Option, // If none, then defaults for shimexecutionmode are used + vaa_args: Option>, // If none, then defaults for shimexecutionmode are used transfer_direction: TransferDirection, ) -> (TestingEngineState, ProgramTestContext, TestingEngine) { let (place_initial_offer_state, mut test_context, testing_engine) = match shim_execution_mode { diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs index be742c124..b0b0d8bdf 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs @@ -144,10 +144,10 @@ pub async fn test_local_token_router_endpoint_creation() { #[tokio::test] pub async fn test_setup_vaas() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifySignature, transfer_direction, @@ -196,10 +196,10 @@ pub async fn test_post_message_shims() { /// Test that the approve usdc helper function works correctly #[tokio::test] pub async fn test_approve_usdc() { - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, TransferDirection::FromArbitrumToEthereum, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index fed516860..8db945b94 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -1,9 +1,7 @@ #![allow(clippy::expect_used)] #![allow(clippy::panic)] // TODO: -// Test that it is possible to continue to prepare order response and execution after initial offer is placed and is paused // Test that auction is expired means that you cannot place offer or execute it -// Cannot place initial offer twice //! # Place initial offer and improve offer instruction testing //! @@ -110,12 +108,12 @@ pub async fn test_place_initial_offer_shimless() { /// Test that auction account is exactly the same when using shimless and fallback instructions #[tokio::test] -pub async fn test_place_initial_offer_shimless_and_fallback_auctions_are_identical() { +pub async fn test_place_initial_offer_shimless_and_shim_auctions_are_identical() { let shimless_config = PlaceInitialOfferInstructionConfig { actor: TestingActorEnum::Owner, ..PlaceInitialOfferInstructionConfig::default() }; - let fallback_config = PlaceInitialOfferInstructionConfig { + let shim_config = PlaceInitialOfferInstructionConfig { actor: TestingActorEnum::Owner, ..PlaceInitialOfferInstructionConfig::default() }; @@ -124,7 +122,7 @@ pub async fn test_place_initial_offer_shimless_and_fallback_auctions_are_identic ) .await; let (final_state_fallback, mut fallback_test_context, _) = Box::pin(place_initial_offer_shim( - fallback_config, + shim_config, None, TRANSFER_DIRECTION, )) @@ -216,10 +214,10 @@ pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { #[tokio::test] pub async fn test_place_initial_offer_shimless_blocks_shim() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -258,10 +256,10 @@ pub async fn test_place_initial_offer_shimless_blocks_shim() { #[tokio::test] pub async fn test_place_initial_offer_shim_blocks_shimless() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -338,10 +336,10 @@ pub async fn test_place_initial_shim_offer_fails_usdt_mint_address() { #[tokio::test] pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, @@ -474,10 +472,10 @@ pub async fn test_place_initial_offer_shim_fails_when_max_fee_and_amount_in_sum_ #[tokio::test] pub async fn test_place_initial_offer_shim_fails_when_vaa_is_expired() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -498,7 +496,6 @@ pub async fn test_place_initial_offer_shim_fails_when_vaa_is_expired() { .execute(&mut test_context, instruction_triggers, None) .await; testing_engine - .testing_context .make_fast_transfer_vaa_expired(&mut test_context, 60) // 1 minute after expiry .await; @@ -526,10 +523,10 @@ pub async fn test_place_initial_offer_shim_fails_when_vaa_is_expired() { #[tokio::test] pub async fn test_place_initial_offer_shim_fails_custodian_is_paused() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -578,6 +575,70 @@ pub async fn test_place_initial_offer_shim_fails_custodian_is_paused() { .await; } +/// Test place initial offer shim fails back to back +#[tokio::test] +pub async fn test_place_initial_offer_shim_fails_back_to_back() { + let (initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + TRANSFER_DIRECTION, + )) + .await; + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: "Already in use".to_string(), + }; + let place_initial_offer_config = PlaceInitialOfferInstructionConfig { + expected_error: Some(expected_error), + ..PlaceInitialOfferInstructionConfig::default() + }; + let instruction_triggers = vec![InstructionTrigger::PlaceInitialOfferShim( + place_initial_offer_config, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initial_offer_state), + ) + .await; +} + +/// Test place initial offer shim fails back to back +#[tokio::test] +pub async fn test_place_initial_offer_shimless_fails_back_to_back() { + let (initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shimless( + PlaceInitialOfferInstructionConfig::default(), + None, + TRANSFER_DIRECTION, + )) + .await; + + let expected_error = ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: "Already in use".to_string(), + }; + let place_initial_offer_config = PlaceInitialOfferInstructionConfig { + expected_error: Some(expected_error), + ..PlaceInitialOfferInstructionConfig::default() + }; + let instruction_triggers = vec![InstructionTrigger::PlaceInitialOfferShimless( + place_initial_offer_config, + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(initial_offer_state), + ) + .await; +} + /// Test that improved offer fails when improvement is too small #[tokio::test] pub async fn test_improve_offer_shim_fails_carping() { @@ -859,12 +920,14 @@ Helper structs and functions pub async fn place_initial_offer_shim( config: PlaceInitialOfferInstructionConfig, - vaa_args: Option, + vaa_args: Option>, transfer_direction: TransferDirection, ) -> (TestingEngineState, ProgramTestContext, TestingEngine) { - let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { - post_vaa: false, - ..VaaArgs::default() + let vaa_args = vaa_args.unwrap_or_else(|| { + vec![VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }] }); let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, @@ -897,12 +960,14 @@ pub async fn place_initial_offer_shim( pub async fn place_initial_offer_shimless( config: PlaceInitialOfferInstructionConfig, - vaa_args: Option, + vaa_args: Option>, transfer_direction: TransferDirection, ) -> (TestingEngineState, ProgramTestContext, TestingEngine) { - let vaa_args = vaa_args.unwrap_or_else(|| VaaArgs { - post_vaa: true, - ..VaaArgs::default() + let vaa_args = vaa_args.unwrap_or_else(|| { + vec![VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }] }); let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, @@ -1000,7 +1065,7 @@ impl TestAuctionSetup { pub fn create_vaa_args_and_initial_offer_config( &self, - ) -> (VaaArgs, PlaceInitialOfferInstructionConfig) { + ) -> (Vec, PlaceInitialOfferInstructionConfig) { let create_deposit_and_fast_transfer_params = CreateDepositAndFastTransferParams { deposit_params: CreateDepositParams { amount: self.deposit_amount, @@ -1013,11 +1078,11 @@ impl TestAuctionSetup { init_auction_fee: self.init_auction_fee, }, }; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: self.post_vaa, create_deposit_and_fast_transfer_params, ..Default::default() - }; + }]; let initial_offer_config = PlaceInitialOfferInstructionConfig { offer_price: self.offer_price, ..PlaceInitialOfferInstructionConfig::default() diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index d1d4a404d..0ee409d26 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -22,11 +22,16 @@ use crate::test_scenarios::execute_order::{execute_order_helper, ShimExecutionMo use crate::test_scenarios::make_offer::place_initial_offer_shim; use crate::testing_engine; use crate::testing_engine::config::{ - InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, PrepareOrderInstructionConfig, + InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, + PrepareOrderResponseInstructionConfig, }; -use crate::utils; +use crate::utils::public_keys::ChainAddress; +use crate::utils::{self, Chain}; +use matching_engine::error::MatchingEngineError; +use ruint::aliases::U256; use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; use solana_sdk::transaction::TransactionError; use testing_engine::config::*; use testing_engine::engine::{InstructionTrigger, TestingEngine}; @@ -65,12 +70,12 @@ use super::make_offer::reopen_fast_market_order_shim; /// Test that the prepare order shim instruction works correctly (from ethereum to arbitrum) #[tokio::test] -pub async fn test_prepare_order_shim() { +pub async fn test_prepare_order_shimful() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -88,7 +93,7 @@ pub async fn test_prepare_order_shim() { ), InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), ]; testing_engine .execute(&mut test_context, instruction_triggers, None) @@ -99,10 +104,10 @@ pub async fn test_prepare_order_shim() { #[tokio::test] pub async fn test_prepare_order_shimless() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -120,7 +125,7 @@ pub async fn test_prepare_order_shimless() { ), InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderResponseInstructionConfig::default()), ]; testing_engine .execute(&mut test_context, instruction_triggers, None) @@ -142,7 +147,7 @@ pub async fn test_prepare_order_response_shim_after_execute_order_shimless() { InstructionTrigger::InitializeFastMarketOrderShim( InitializeFastMarketOrderShimInstructionConfig::default(), ), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), ]; testing_engine .execute( @@ -160,15 +165,15 @@ pub async fn test_prepare_order_response_shimless_after_execute_order_shim() { let (execute_order_state, mut test_context, testing_engine) = Box::pin(execute_order_helper( ExecuteOrderInstructionConfig::default(), ShimExecutionMode::Shim, - Some(VaaArgs { + Some(vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }), + }]), transfer_direction, )) .await; let instruction_triggers = vec![InstructionTrigger::PrepareOrderShimless( - PrepareOrderInstructionConfig::default(), + PrepareOrderResponseInstructionConfig::default(), )]; testing_engine .execute( @@ -199,7 +204,7 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ .await; let instruction_triggers = vec![ InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), ]; testing_engine .execute( @@ -271,7 +276,7 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ )) .await; let instruction_triggers = vec![InstructionTrigger::PrepareOrderShim( - PrepareOrderInstructionConfig::default(), + PrepareOrderResponseInstructionConfig::default(), )]; testing_engine .execute( @@ -282,6 +287,34 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ .await; } +/// Test prepare order response shim after custodian is paused after initial offer +#[tokio::test] +pub async fn test_prepare_order_response_shim_after_custodian_is_paused_after_initial_offer() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![ + InstructionTrigger::SetPauseCustodian(SetPauseCustodianInstructionConfig { + is_paused: true, + ..Default::default() + }), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + /* Sad path tests section @@ -314,10 +347,10 @@ pub async fn test_prepare_order_response_shim_after_reopening_fast_market_order_ #[tokio::test] pub async fn test_prepare_order_response_shimless_blocks_shimful() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -335,15 +368,15 @@ pub async fn test_prepare_order_response_shimless_blocks_shimful() { ), InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderResponseInstructionConfig::default()), // TODO: Figure out why this is failing on account already in use rather than the what happens the other way around above - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig { + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { expected_error: Some(ExpectedError { instruction_index: 0, error_code: 0, error_string: TransactionError::AccountInUse.to_string(), }), - ..PrepareOrderInstructionConfig::default() + ..PrepareOrderResponseInstructionConfig::default() }), ]; testing_engine @@ -355,10 +388,10 @@ pub async fn test_prepare_order_response_shimless_blocks_shimful() { #[tokio::test] pub async fn test_prepare_order_response_shimful_blocks_shimless() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -376,13 +409,190 @@ pub async fn test_prepare_order_response_shimful_blocks_shimless() { ), InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig { + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless(PrepareOrderResponseInstructionConfig { expected_log_messages: Some(vec![ExpectedLog { log_message: "Already prepared".to_string(), count: 1, }]), - ..PrepareOrderInstructionConfig::default() + ..PrepareOrderResponseInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; +} + +/// Cannot prepare order response with emitter chain mismatch +#[tokio::test] +pub async fn test_prepare_order_response_shim_emitter_chain_mismatch() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = vec![ + VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }, + VaaArgs { + post_vaa: false, + override_emitter_chain_and_address: Some(ChainAddress::from_registered_token_router( + Chain::Arbitrum, + )), + ..VaaArgs::default() + }, + ]; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig { + vaa_index: 0, + ..ExecuteOrderInstructionConfig::default() + }), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + vaa_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::InvalidCctpMessage), + error_string: "Invalid cctp message".to_string(), + }), + ..PrepareOrderResponseInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; +} + +#[tokio::test] +pub async fn test_prepare_order_response_shimless_emitter_chain_mismatch() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = vec![ + VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }, + VaaArgs { + post_vaa: true, + override_emitter_chain_and_address: Some(ChainAddress::from_registered_token_router( + Chain::Arbitrum, + )), + ..VaaArgs::default() + }, + ]; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShimless(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig { + vaa_index: 0, + ..ExecuteOrderInstructionConfig::default() + }), + InstructionTrigger::PrepareOrderShimless(PrepareOrderResponseInstructionConfig { + vaa_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::InvalidSourceRouter), + error_string: "Invalid source router".to_string(), + }), + ..PrepareOrderResponseInstructionConfig::default() + }), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; +} + +/// Cannot prepare order response with deposit cctp nonce mismatch +#[tokio::test] +pub async fn test_prepare_order_response_shim_deposit_cctp_nonce_mismatch() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let vaa_args = vec![ + VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }, + VaaArgs { + post_vaa: false, + sequence: Some(69), + cctp_nonce: Some(16), + create_deposit_and_fast_transfer_params: + utils::vaa::CreateDepositAndFastTransferParams { + deposit_params: utils::vaa::CreateDepositParams { + amount: U256::from(10000), + base_fee: 10, + }, + fast_transfer_params: utils::vaa::CreateFastTransferParams { + ..utils::vaa::CreateFastTransferParams { + amount_in: 100, + max_fee: 12, + init_auction_fee: 1, + ..utils::vaa::CreateFastTransferParams::default() + } + }, + }, + ..VaaArgs::default() + }, + ]; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig { + vaa_index: 0, + ..ExecuteOrderInstructionConfig::default() + }), + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig { + fast_market_order_id: 1, + vaa_index: 1, + close_account_refund_recipient: Some(Pubkey::new_unique()), + ..InitializeFastMarketOrderShimInstructionConfig::default() + }, + ), + // TODO: Figure out if this is wrong. The cctp message is + // It currently fails because no auction has been created on this account so therefore the custodian is not the authority + // and therefore cannot prepare the order at the transfer instruction + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + vaa_index: 1, + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(MatchingEngineError::InvalidCctpMessage), + error_string: "Invalid cctp message".to_string(), + }), + ..PrepareOrderResponseInstructionConfig::default() }), ]; testing_engine diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index 8b17544d8..efa0b1ed6 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -53,10 +53,10 @@ use utils::vaa::VaaArgs; #[tokio::test] pub async fn test_settle_auction_complete() { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -75,7 +75,7 @@ pub async fn test_settle_auction_complete() { ), InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), ]; testing_engine @@ -83,12 +83,36 @@ pub async fn test_settle_auction_complete() { .await; } +/// Test settle auction works when custodian is paused +#[tokio::test] +pub async fn test_settle_auction_custodian_paused() { + let (initial_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + TransferDirection::FromEthereumToArbitrum, + )) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::SetPauseCustodian(SetPauseCustodianInstructionConfig { + is_paused: true, + ..Default::default() + }), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute(&mut test_context, instruction_triggers, Some(initial_state)) + .await; +} + /// Test that the settle auction instruction works with reopened fast market order #[tokio::test] pub async fn test_settle_auction_reopened_fast_market_order() { let (initial_state, mut test_context, testing_engine) = Box::pin(place_initial_offer_shim( PlaceInitialOfferInstructionConfig::default(), - Some(VaaArgs::default()), + Some(vec![VaaArgs::default()]), TransferDirection::FromEthereumToArbitrum, )) .await; @@ -103,7 +127,7 @@ pub async fn test_settle_auction_reopened_fast_market_order() { let instruction_triggers = vec![ InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), ]; testing_engine @@ -131,10 +155,10 @@ mod helpers { pub async fn balance_changes_shim() -> BalanceChanges { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -178,7 +202,7 @@ mod helpers { ); let instruction_triggers = vec![ InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShim(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), ]; let prepare_order_state = testing_engine .execute( @@ -248,10 +272,10 @@ mod helpers { pub async fn balance_changes_shimless() -> BalanceChanges { let transfer_direction = TransferDirection::FromEthereumToArbitrum; - let vaa_args = VaaArgs { + let vaa_args = vec![VaaArgs { post_vaa: true, ..VaaArgs::default() - }; + }]; let (testing_context, mut test_context) = setup_environment( ShimMode::VerifyAndPostSignature, transfer_direction, @@ -288,7 +312,9 @@ mod helpers { ); let instruction_triggers = vec![ InstructionTrigger::ExecuteOrderShimless(ExecuteOrderInstructionConfig::default()), - InstructionTrigger::PrepareOrderShimless(PrepareOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShimless( + PrepareOrderResponseInstructionConfig::default(), + ), InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), ]; testing_engine diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 3bbc48ffc..31575be81 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -22,7 +22,7 @@ use std::{ use crate::{ shimless::initialize::AuctionParametersConfig, - utils::{auction::ActiveAuctionState, token_account::SplTokenEnum, Chain}, + utils::{token_account::SplTokenEnum, Chain}, }; use anchor_lang::prelude::*; use solana_program_test::ProgramTestContext; @@ -118,6 +118,7 @@ impl InstructionConfig for CreateCctpRouterEndpointsInstructionConfig { pub struct InitializeFastMarketOrderShimInstructionConfig { pub fast_market_order_id: u32, pub close_account_refund_recipient: Option, // If none defaults to solver 0 pubkey, + pub vaa_index: usize, // If none defaults to 0 pub payer_signer: Option>, // If none defaults to owner keypair pub expected_error: Option, // If none, will not check for an error pub expected_log_messages: Option>, // If none, will not check for logs @@ -150,16 +151,17 @@ impl InstructionConfig for SetPauseCustodianInstructionConfig { } #[derive(Clone, Default)] -pub struct PrepareOrderInstructionConfig { +pub struct PrepareOrderResponseInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, pub actor_enum: TestingActorEnum, pub token_enum: SplTokenEnum, + pub vaa_index: usize, pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, } -impl InstructionConfig for PrepareOrderInstructionConfig { +impl InstructionConfig for PrepareOrderResponseInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() } @@ -168,16 +170,33 @@ impl InstructionConfig for PrepareOrderInstructionConfig { } } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct ExecuteOrderInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, pub actor_enum: TestingActorEnum, pub token_enum: SplTokenEnum, + pub vaa_index: usize, + pub fast_forward_slots: u64, // Number of slots to fast forward, defaults to 3 in Default impl pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, } +impl Default for ExecuteOrderInstructionConfig { + fn default() -> Self { + Self { + fast_forward_slots: 3, + actor_enum: TestingActorEnum::default(), + fast_market_order_address: None, + token_enum: SplTokenEnum::default(), + vaa_index: 0, + payer_signer: None, + expected_error: None, + expected_log_messages: None, + } + } +} + impl InstructionConfig for ExecuteOrderInstructionConfig { fn expected_error(&self) -> Option<&ExpectedError> { self.expected_error.as_ref() @@ -374,7 +393,29 @@ impl InstructionConfig for ImproveOfferInstructionConfig { pub struct VerifyBalancesConfig { pub previous_state_balances: Balances, - pub balance_changes: BalanceChanges, + pub balance_changes_config: BalanceChangesConfig, + pub closed_token_account_enums: Option>, +} + +pub struct BalanceChangesConfig { + pub actor: TestingActor, + pub spl_token_enum: SplTokenEnum, + pub custodian_token_previous_balance: u64, +} + +impl VerifyBalancesConfig { + pub async fn get_balance_changes( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + ) -> BalanceChanges { + BalanceChanges::execute_order_changes( + test_context, + current_state, + &self.balance_changes_config, + ) + .await + } } pub struct ExecuteOrderActorEnums { @@ -383,6 +424,16 @@ pub struct ExecuteOrderActorEnums { pub initial_offer: TestingActorEnum, } +impl ExecuteOrderActorEnums { + pub fn from_state(state: &TestingEngineState) -> Self { + Self { + executor: state.execute_order_actor().unwrap(), + best_offer: state.best_offer_actor().unwrap(), + initial_offer: state.initial_offer_placed_actor().unwrap(), + } + } +} + #[derive(PartialEq, Eq, Debug, Clone)] pub struct BalanceChanges(HashMap); @@ -454,27 +505,35 @@ impl BalanceChanges { pub async fn execute_order_changes( test_context: &mut ProgramTestContext, current_state: &TestingEngineState, - executor: TestingActor, - execute_order_actor_enums: ExecuteOrderActorEnums, - active_auction_state: &ActiveAuctionState, - spl_token_enum: SplTokenEnum, + balance_changes_config: &BalanceChangesConfig, ) -> Self { + let executor = &balance_changes_config.actor; + let spl_token_enum = &balance_changes_config.spl_token_enum; + let executor_testing_actor_enum = ExecuteOrderActorEnums::from_state(current_state); let ExecuteOrderActorEnums { executor: executor_testing_actor_enum, best_offer: best_offer_testing_actor_enum, initial_offer: initial_offer_testing_actor_enum, - } = execute_order_actor_enums; + } = executor_testing_actor_enum; + let active_auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction is not initialized"); // TODO: Make this dynamic so that it does not depend on the first vaa pair let fast_market_order = current_state .base() .get_fast_market_order(0) .expect("Fast market order is not initialized"); let init_auction_fee = fast_market_order.init_auction_fee; - let executor_token_address = executor.token_account_address(&spl_token_enum).unwrap(); + let executor_token_address = executor.token_account_address(spl_token_enum).unwrap(); let auction_calculations = active_auction_state - .get_auction_calculations(test_context, executor_token_address, init_auction_fee) + .get_auction_calculations( + test_context, + executor_token_address, + balance_changes_config.custodian_token_previous_balance, + init_auction_fee, + ) .await; - println!("auction_calculations: {:?}", auction_calculations); let mut balance_changes = HashMap::new(); balance_changes.insert( diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index d9133571f..d04304da7 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -54,8 +54,8 @@ pub enum InstructionTrigger { ImproveOfferShimless(ImproveOfferInstructionConfig), ExecuteOrderShimless(ExecuteOrderInstructionConfig), ExecuteOrderShim(ExecuteOrderInstructionConfig), - PrepareOrderShimless(PrepareOrderInstructionConfig), - PrepareOrderShim(PrepareOrderInstructionConfig), + PrepareOrderShimless(PrepareOrderResponseInstructionConfig), + PrepareOrderShim(PrepareOrderResponseInstructionConfig), SettleAuction(SettleAuctionInstructionConfig), CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), } @@ -64,7 +64,7 @@ pub enum VerificationTrigger { // Verify that the auction state is as expected (bool is expected to succeed) VerifyAuctionState(bool), // Verify that the execute order math is correct - VerifyBalances(VerifyBalancesConfig), + VerifyBalances(Box), } pub enum ExecutionTrigger { @@ -470,8 +470,8 @@ impl TestingEngine { current_state: &TestingEngineState, config: &InitializeFastMarketOrderShimInstructionConfig, ) -> TestingEngineState { - let first_test_vaa_pair = current_state.get_first_test_vaa_pair(); - let fast_transfer_vaa = first_test_vaa_pair.fast_transfer_vaa.clone(); + let test_vaa_pair = current_state.get_test_vaa_pair(config.vaa_index); + let fast_transfer_vaa = test_vaa_pair.fast_transfer_vaa.clone(); let fast_market_order = create_fast_market_order_state_from_vaa_data( &fast_transfer_vaa.vaa_data, config @@ -725,7 +725,7 @@ impl TestingEngine { current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { - let result = shimful::shims_execute_order::execute_order_fallback_test( + let result = shimful::shims_execute_order::execute_order_shimful_test( &self.testing_context, test_context, current_state, @@ -764,10 +764,6 @@ impl TestingEngine { current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_config_address = current_state .auction_config_address() .expect("Auction config address not found"); @@ -783,7 +779,7 @@ impl TestingEngine { let auction_accounts = AuctionAccounts::new( Some( current_state - .get_first_test_vaa_pair() + .get_test_vaa_pair(config.vaa_index) .fast_transfer_vaa .get_vaa_pubkey(), ), @@ -798,9 +794,9 @@ impl TestingEngine { let result = shimless::execute_order::execute_order_shimless_test( &self.testing_context, test_context, + config, &auction_accounts, current_state.auction_state(), - &payer_signer, config.expected_error(), ) .await; @@ -831,39 +827,19 @@ impl TestingEngine { &self, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, - config: &PrepareOrderInstructionConfig, + config: &PrepareOrderResponseInstructionConfig, ) -> TestingEngineState { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - - let deposit_vaa = current_state.get_first_test_vaa_pair().deposit_vaa.clone(); - let deposit_vaa_data = deposit_vaa.get_vaa_data(); - let deposit = deposit_vaa - .payload_deserialized - .clone() - .unwrap() - .get_deposit() - .unwrap(); - - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - let result = shimful::shims_prepare_order_response::prepare_order_response_test( &self.testing_context, test_context, - &payer_signer, - deposit_vaa_data, + config, current_state, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, - &deposit, - config.expected_error(), ) .await; if config.expected_error.is_none() { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); let prepare_order_response_fixture = result.unwrap(); let order_prepared_state = OrderPreparedState { prepared_order_response_address: prepare_order_response_fixture @@ -891,12 +867,8 @@ impl TestingEngine { &self, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, - config: &PrepareOrderInstructionConfig, + config: &PrepareOrderResponseInstructionConfig, ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_accounts = current_state .auction_accounts() .expect("Auction accounts not found"); @@ -909,13 +881,9 @@ impl TestingEngine { let result = shimless::prepare_order_response::prepare_order_response( &self.testing_context, test_context, - &payer_signer, + config, current_state, - &auction_accounts.to_router_endpoint, - &auction_accounts.from_router_endpoint, &solver_token_account, - config.expected_error(), - config.expected_log_messages.as_ref(), ) .await; if config.expected_error.is_none() { @@ -1001,24 +969,99 @@ impl TestingEngine { ) -> TestingEngineState { let previous_state_balances = &config.previous_state_balances; let balances = self.testing_context.get_balances(test_context).await; - for (actor, balance_change) in config.balance_changes.iter() { + let balance_changes = config + .get_balance_changes(test_context, current_state) + .await; + let mut is_error = false; + for (actor, balance_change) in balance_changes.iter() { + if let Some(closed_token_account_enums) = &config.closed_token_account_enums { + if closed_token_account_enums.contains(actor) { + continue; + } + } let balance = balances.get(actor).unwrap(); let previous_balance = previous_state_balances.get(actor).unwrap(); - assert_eq!( - balance.usdc, - saturating_add_signed(previous_balance.usdc, balance_change.usdc), - "USDC balance mismatch for actor {:?}", - actor - ); - assert_eq!( - balance.usdt, - saturating_add_signed(previous_balance.usdt, balance_change.usdt), - "USDT balance mismatch for actor {:?}", - actor - ); + if balance.usdc != saturating_add_signed(previous_balance.usdc, balance_change.usdc) { + is_error = true; + println!("USDC balance mismatch for actor {:?}", actor); + println!("Expected balance change: {:?}", balance_change.usdc); + println!( + "Actual balance change: {:?}", + balance.usdc.saturating_sub(previous_balance.usdc) + ); + } + if balance.usdt != saturating_add_signed(previous_balance.usdt, balance_change.usdt) { + is_error = true; + println!("USDT balance mismatch for actor {:?}", actor); + println!("Expected balance change: {:?}", balance_change.usdt); + println!( + "Actual balance change: {:?}", + balance.usdt.saturating_sub(previous_balance.usdt) + ); + } + } + if is_error { + panic!("Balance mismatch"); } current_state.clone() } + + pub async fn make_auction_passed_penalty_period( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + slots_after_expiry: u64, + ) { + let active_auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction state expected"); + let auction_expiration_slot = active_auction_state + .get_auction_expiration_slot(test_context) + .await; + let target_slot = auction_expiration_slot + slots_after_expiry; + fast_forward_slots(test_context, target_slot).await; + } + + pub async fn make_auction_passed_grace_period( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + slots_after_grace_period: u64, + ) { + let active_auction_state = current_state + .auction_state() + .get_active_auction() + .expect("Active auction state expected"); + let auction_grace_period_slot = active_auction_state + .get_auction_grace_period_slot(test_context) + .await; + let target_slot = auction_grace_period_slot + slots_after_grace_period; + fast_forward_slots(test_context, target_slot).await; + } + + pub async fn make_fast_transfer_vaa_expired( + &self, + test_context: &mut ProgramTestContext, + seconds_after_expiry: i64, + ) { + self.testing_context + .make_fast_transfer_vaa_expired(test_context, seconds_after_expiry) + .await; + } + + pub async fn close_token_account( + &self, + test_context: &mut ProgramTestContext, + actor_enum: &TestingActorEnum, + spl_token_enum: &SplTokenEnum, + ) { + self.testing_context + .testing_actors + .get_actor(actor_enum) + .close_token_account(test_context, spl_token_enum) + .await; + } } /// Fast forwards the slot in the test context diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index c93de196e..569d70c43 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -15,6 +15,7 @@ use crate::testing_engine::config::{ExpectedError, ExpectedLog}; use crate::utils::account_fixtures::FixtureAccounts; use crate::utils::airdrop::{airdrop, airdrop_spl_token}; +use crate::utils::cctp_message::CctpRemoteTokenMessenger; use crate::utils::mint::MintFixture; use crate::utils::program_fixtures::{ initialise_cctp_message_transmitter, initialise_cctp_token_messenger_minter, @@ -464,6 +465,34 @@ impl TestingContext { self.fast_forward_to_timestamp(test_context, target_timestamp) .await; } + + pub async fn get_remote_token_messenger( + &self, + test_context: &mut ProgramTestContext, + ) -> CctpRemoteTokenMessenger { + let fixture_accounts = self.get_fixture_accounts().unwrap(); + match self.transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + crate::utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.ethereum_remote_token_messenger, + ) + .await + .into() + } + TransferDirection::FromArbitrumToEthereum => { + crate::utils::router::get_remote_token_messenger( + test_context, + fixture_accounts.arbitrum_remote_token_messenger, + ) + .await + .into() + } + TransferDirection::Other => { + panic!("Unsupported transfer direction"); + } + } + } } /// A struct representing a solver @@ -594,14 +623,17 @@ impl TestingActor { spl_token_enum: &SplTokenEnum, ) -> u64 { if let Some(token_account) = self.token_account_address(spl_token_enum) { - let account = test_context + if let Some(account) = test_context .banks_client .get_account(token_account) .await .unwrap() - .unwrap(); - let token_account = TokenAccount::try_deserialize(&mut &account.data[..]).unwrap(); - token_account.amount + { + let token_account = TokenAccount::try_deserialize(&mut &account.data[..]).unwrap(); + token_account.amount + } else { + 0 + } } else { 0 } @@ -655,6 +687,65 @@ impl TestingActor { .await .expect("Failed to approve USDC"); } + + pub async fn close_token_account( + &self, + test_context: &mut ProgramTestContext, + spl_token_enum: &SplTokenEnum, + ) { + if let Some(token_account) = self.token_account_address(spl_token_enum) { + let balance = self + .get_token_account_balance(test_context, spl_token_enum) + .await; + let burn_ix = spl_token::instruction::burn( + &spl_token::ID, + &token_account, + &USDC_MINT_ADDRESS, + &self.pubkey(), + &[], + balance, + ) + .unwrap(); + let last_blockhash = test_context + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let transaction = Transaction::new_signed_with_payer( + &[burn_ix], + Some(&self.pubkey()), + &[&self.keypair()], + last_blockhash, + ); + test_context + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to burn token account"); + let close_account_ix = spl_token::instruction::close_account( + &spl_token::ID, + &token_account, + &self.pubkey(), + &self.pubkey(), + &[], + ) + .unwrap(); + let last_blockhash = test_context + .get_new_latest_blockhash() + .await + .expect("Failed to get new blockhash"); + let transaction = Transaction::new_signed_with_payer( + &[close_account_ix], + Some(&self.pubkey()), + &[&self.keypair()], + last_blockhash, + ); + test_context + .banks_client + .process_transaction(transaction) + .await + .expect("Failed to close token account"); + } + } } /// A struct containing the balances of all the test actors @@ -866,6 +957,17 @@ impl TestingActors { } } + pub fn get_actor(&self, actor_enum: &TestingActorEnum) -> &TestingActor { + match actor_enum { + TestingActorEnum::Owner => &self.owner, + TestingActorEnum::OwnerAssistant => &self.owner_assistant, + TestingActorEnum::FeeRecipient => &self.fee_recipient, + TestingActorEnum::Relayer => &self.relayer, + TestingActorEnum::Solver(index) => &self.solvers[*index].actor, + TestingActorEnum::Liquidator => &self.liquidator, + } + } + /// Add solvers to the testing actors #[allow(dead_code)] pub async fn add_solvers( @@ -940,54 +1042,59 @@ impl Default for TransferDirection { pub async fn setup_environment( shim_mode: ShimMode, transfer_direction: TransferDirection, - vaa_args: Option, + vaa_args: Option>, ) -> (TestingContext, ProgramTestContext) { let mut pre_testing_context = PreTestingContext::new(PROGRAM_ID, OWNER_KEYPAIR_PATH); let vaas_test: Option = match vaa_args { - Some(vaa_args) => { - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum] - .clone() - .try_into() - .expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum] - .clone() - .try_into() - .expect("Failed to convert registered token router address to bytes [u8; 32]"); - match transfer_direction { - TransferDirection::FromArbitrumToEthereum => { - Some(create_vaas_test_with_chain_and_address( - &mut pre_testing_context.program_test, - USDC_MINT_ADDRESS, - CCTP_MINT_RECIPIENT, - ChainAndAddress { - chain: Chain::Arbitrum, - address: arbitrum_emitter_address, - }, - ChainAndAddress { - chain: Chain::Ethereum, - address: ethereum_emitter_address, - }, - vaa_args, - )) - } - TransferDirection::FromEthereumToArbitrum => { - Some(create_vaas_test_with_chain_and_address( - &mut pre_testing_context.program_test, - USDC_MINT_ADDRESS, - CCTP_MINT_RECIPIENT, - ChainAndAddress { - chain: Chain::Ethereum, - address: ethereum_emitter_address, - }, - ChainAndAddress { - chain: Chain::Arbitrum, - address: arbitrum_emitter_address, - }, - vaa_args, - )) - } - TransferDirection::Other => panic!("Unsupported transfer direction"), + Some(vaa_args_plural) => { + let mut vaas_test_temp = TestVaaPairs::new(); + for vaa_args in vaa_args_plural { + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum] + .clone() + .try_into() + .expect("Failed to convert registered token router address to bytes [u8; 32]"); + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum] + .clone() + .try_into() + .expect("Failed to convert registered token router address to bytes [u8; 32]"); + let new_vaas_test = match transfer_direction { + TransferDirection::FromArbitrumToEthereum => { + create_vaas_test_with_chain_and_address( + &mut pre_testing_context.program_test, + USDC_MINT_ADDRESS, + CCTP_MINT_RECIPIENT, + ChainAndAddress { + chain: Chain::Arbitrum, + address: arbitrum_emitter_address, + }, + ChainAndAddress { + chain: Chain::Ethereum, + address: ethereum_emitter_address, + }, + vaa_args, + ) + } + TransferDirection::FromEthereumToArbitrum => { + create_vaas_test_with_chain_and_address( + &mut pre_testing_context.program_test, + USDC_MINT_ADDRESS, + CCTP_MINT_RECIPIENT, + ChainAndAddress { + chain: Chain::Ethereum, + address: ethereum_emitter_address, + }, + ChainAndAddress { + chain: Chain::Arbitrum, + address: arbitrum_emitter_address, + }, + vaa_args, + ) + } + TransferDirection::Other => panic!("Unsupported transfer direction"), + }; + vaas_test_temp.extend(new_vaas_test.0); } + Some(vaas_test_temp) } None => None, }; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 8613dac96..99c9f2e21 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -343,8 +343,8 @@ impl TestingEngineState { } } - pub fn get_first_test_vaa_pair(&self) -> &TestVaaPair { - self.base().vaas.first().unwrap() + pub fn get_test_vaa_pair(&self, index: usize) -> &TestVaaPair { + self.base().vaas.get(index).unwrap() } // Convenience methods for common fields diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 6299dc121..02b880992 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -136,6 +136,7 @@ impl ActiveAuctionState { &self, test_context: &mut ProgramTestContext, executor_token_address: Pubkey, + custodian_token_balance_previous: u64, init_auction_fee: u64, ) -> AuctionCalculations { let auction_info = helpers::get_auction_info(test_context, self.auction_address).await; @@ -148,13 +149,12 @@ impl ActiveAuctionState { let initial_offer_token_account_exists = helpers::token_account_exists(test_context, self.initial_offer.offer_token).await; - let custody_token_balance = - helpers::get_token_account_balance(test_context, self.auction_custody_token_address) - .await; + let custody_token_balance = custodian_token_balance_previous; // Cast to u64 for math later let amount_in = auction_info.amount_in; let grace_period = u64::from(auction_config.grace_period); + let auction_duration = u64::from(auction_config.duration); let initial_penalty_bps = u64::from(auction_config.initial_penalty_bps); let penalty_period = u64::from(auction_config.penalty_period); let user_penalty_reward_bps = u64::from(auction_config.user_penalty_reward_bps); @@ -163,9 +163,11 @@ impl ActiveAuctionState { let security_deposit_bps = u64::from(auction_config.security_deposit_bps); let latest_slot = test_context.banks_client.get_root_slot().await.unwrap(); - let slots_elapsed = latest_slot.saturating_sub(auction_info.start_slot); + let slots_elapsed = latest_slot + .saturating_sub(auction_info.start_slot) + .saturating_sub(auction_duration); let elapsed_penalty_period = slots_elapsed.saturating_sub(grace_period); - let has_penalty = elapsed_penalty_period >= penalty_period; + let has_penalty = slots_elapsed >= grace_period; // Copy of computeDepositPenalty let (penalty_amount, user_reward) = if has_penalty { @@ -196,12 +198,12 @@ impl ActiveAuctionState { .unwrap(); // (security_deposit - base_penalty) * elapsed_penalty_period / penalty_period let pre_penalty_amount = base_penalty .checked_add(penalty_period_elapsed_penalty) - .unwrap(); + .unwrap(); // base_penalty + penalty_period_elapsed_penalty let user_reward = pre_penalty_amount .checked_mul(user_penalty_reward_bps) .unwrap() .checked_div(Self::BPS_DENOMINATOR) - .unwrap(); + .unwrap(); // pre_penalty_amount * user_penalty_reward_bps / 10000 ( pre_penalty_amount.checked_sub(user_reward).unwrap(), user_reward, @@ -229,7 +231,12 @@ impl ActiveAuctionState { let mut initial_offer_token_balance_change: i32 = 0; let mut deposit_and_fee = if has_penalty { - i32::try_from(security_deposit.saturating_sub(user_reward)).unwrap() + i32::try_from( + security_deposit + .saturating_add(self.best_offer.offer_price) + .saturating_sub(user_reward), + ) + .unwrap() } else { i32::try_from(security_deposit.saturating_add(self.best_offer.offer_price)).unwrap() }; @@ -257,9 +264,10 @@ impl ActiveAuctionState { .saturating_sub(amount_in); // If the best offer token is not the same as the initial offer token, and the initial offer token account exists, subtract the init auction fee - if self.best_offer.offer_token != self.initial_offer.offer_token + if executor_token_address != self.initial_offer.offer_token && initial_offer_token_account_exists { + // Don't give the init auction fee to the executor if the initial offer token exists and is not the same as the executor custody_token_balance_change = custody_token_balance_change.saturating_sub(init_auction_fee); } @@ -279,7 +287,9 @@ impl ActiveAuctionState { best_offer_token_balance_change = balance_change; // If the all token accounts are the same, apply the same balance change to each of them - if self.initial_offer.offer_token == self.best_offer.offer_token { + if self.initial_offer.offer_token == self.best_offer.offer_token + && initial_offer_token_account_exists + { initial_offer_token_balance_change = balance_change; } @@ -302,8 +312,7 @@ impl ActiveAuctionState { // If the initial offer token is the same as the best offer token, apply the same balance change to each of them if self.initial_offer.offer_token == self.best_offer.offer_token { - let balance_change = - deposit_and_fee + init_auction_fee + amount_in + security_deposit; + let balance_change = deposit_and_fee + init_auction_fee; // This is sufficient, because either neither of them exist or both do if best_offer_token_account_exists { best_offer_token_balance_change = balance_change; @@ -314,18 +323,34 @@ impl ActiveAuctionState { best_offer_token_balance_change = deposit_and_fee; }; if initial_offer_token_account_exists { - initial_offer_token_balance_change = init_auction_fee; + if executor_token_address == self.initial_offer.offer_token { + initial_offer_token_balance_change = executor_token_balance_change; + } else { + initial_offer_token_balance_change = init_auction_fee; + } } } } // If there is no penalty - } else if self.best_offer.offer_token == self.initial_offer.offer_token { + } else if self.best_offer.offer_token == self.initial_offer.offer_token + && initial_offer_token_account_exists + { let balance_change = deposit_and_fee + init_auction_fee; best_offer_token_balance_change = balance_change; initial_offer_token_balance_change = balance_change; } else { - best_offer_token_balance_change = deposit_and_fee; - initial_offer_token_balance_change = init_auction_fee; + if best_offer_token_account_exists { + best_offer_token_balance_change = deposit_and_fee; + } else { + executor_token_balance_change = + executor_token_balance_change.saturating_add(deposit_and_fee); + } + if initial_offer_token_account_exists { + initial_offer_token_balance_change = init_auction_fee; + } else { + executor_token_balance_change = + executor_token_balance_change.saturating_add(init_auction_fee); + } } let expected_token_balance_changes = ExpectedTokenBalanceChanges { @@ -348,6 +373,34 @@ impl ActiveAuctionState { has_penalty, } } + + pub async fn get_auction_expiration_slot(&self, test_context: &mut ProgramTestContext) -> u64 { + let auction_info = helpers::get_auction_info(test_context, self.auction_address).await; + let auction_config = + helpers::get_auction_config(test_context, self.auction_config_address).await; + auction_info.start_slot + + u64::from(auction_config.grace_period) + + u64::from(auction_config.penalty_period) + } + + pub async fn get_auction_grace_period_slot( + &self, + test_context: &mut ProgramTestContext, + ) -> u64 { + let auction_info = helpers::get_auction_info(test_context, self.auction_address).await; + let auction_config = + helpers::get_auction_config(test_context, self.auction_config_address).await; + auction_info.start_slot + + u64::from(auction_config.duration) + + u64::from(auction_config.grace_period) + } + + pub async fn get_auction_custody_token_balance( + &self, + test_context: &mut ProgramTestContext, + ) -> u64 { + helpers::get_token_account_balance(test_context, self.auction_custody_token_address).await + } } /// A struct representing an auction offer @@ -569,15 +622,18 @@ mod helpers { test_context: &mut ProgramTestContext, token_address: Pubkey, ) -> u64 { - let token_account = test_context + if let Some(token_account) = test_context .banks_client .get_account(token_address) .await .unwrap() - .unwrap(); - let mut data_ref = token_account.data.as_ref(); - let token_account_data: TokenAccount = - AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); - token_account_data.amount + { + let mut data_ref = token_account.data.as_ref(); + let token_account_data: TokenAccount = + AccountDeserialize::try_deserialize(&mut data_ref).unwrap(); + token_account_data.amount + } else { + 0 + } } } diff --git a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs index 25da65623..aae82edca 100644 --- a/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs +++ b/solana/modules/matching-engine-testing/tests/utils/cctp_message.rs @@ -1,3 +1,5 @@ +use crate::testing_engine::setup::TestingContext; +use crate::testing_engine::state::TestingEngineState; use crate::utils::ETHEREUM_USDC_ADDRESS; use anchor_lang::prelude::*; use common::messages::raw::LiquidityLayerDepositMessage; @@ -11,6 +13,7 @@ use common::wormhole_cctp_solana::cctp::{ use common::wormhole_cctp_solana::messages::Deposit; use matching_engine::state::FastMarketOrder; use num_traits::FromBytes; +use ruint::aliases::U256; use ruint::Uint; use secp256k1::SecretKey as SecpSecretKey; use solana_program::keccak::{Hash, Hasher}; @@ -138,8 +141,8 @@ pub struct CctpRemoteTokenMessenger { pub token_messenger: Pubkey, } -impl From<&RemoteTokenMessenger> for CctpRemoteTokenMessenger { - fn from(value: &RemoteTokenMessenger) -> Self { +impl From for CctpRemoteTokenMessenger { + fn from(value: RemoteTokenMessenger) -> Self { Self { domain: value.domain, token_messenger: Pubkey::from(value.token_messenger), @@ -582,30 +585,59 @@ impl CctpMessage { /// /// # Arguments /// +/// * `testing_context` - The testing context /// * `test_context` - The test context -/// * `source_cctp_domain` - The source CCTP domain -/// * `cctp_nonce` - The nonce of the CCTP message -/// * `amount` - The amount of the token to burn -/// * `message_transmitter_config_pubkey` - The pubkey of the message transmitter config -/// * `remote_token_messenger` - The remote token messenger -/// * `cctp_mint_recipient` - The address of the recipient of the token -/// * `custodian_address` - The address of the custodian -#[allow(clippy::too_many_arguments)] +/// * `current_state` - The current state of the testing engine +/// * `test_vaa_pair_index` - The index of the test VAA pair +/// +/// # Returns +/// +/// A CCTP token burn message pub async fn craft_cctp_token_burn_message( + testing_context: &TestingContext, test_context: &mut ProgramTestContext, - source_cctp_domain: u32, - cctp_nonce: u64, - amount: Uint<256, 4>, // Only allows for 8 byte amounts for now. If we want larger amount support, we can change this to uint256. - message_transmitter_config_pubkey: &Pubkey, - remote_token_messenger: &CctpRemoteTokenMessenger, - cctp_mint_recipient: &Pubkey, - custodian_address: &Pubkey, + current_state: &TestingEngineState, + test_vaa_pair_index: usize, ) -> Result { + let fixture_accounts = testing_context + .fixture_accounts + .clone() + .expect("Fixture accounts not found"); + let remote_token_messenger = testing_context + .get_remote_token_messenger(test_context) + .await; + + let message_transmitter_config_pubkey = fixture_accounts.message_transmitter_config; + + let custodian_address = current_state + .custodian_address() + .expect("Custodian address not found"); + + let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); + + let test_vaa_pair = current_state.get_test_vaa_pair(test_vaa_pair_index); + let deposit = test_vaa_pair + .deposit_vaa + .get_payload_deserialized() + .unwrap() + .get_deposit() + .unwrap(); + let cctp_nonce = deposit.cctp_nonce; + let source_cctp_domain = deposit.source_cctp_domain; + let amount = test_vaa_pair + .fast_transfer_vaa + .get_payload_deserialized() + .unwrap() + .get_fast_transfer() + .unwrap() + .amount_in; + + let amount = U256::from(amount); let destination_cctp_domain = Chain::Solana.as_cctp_domain(); // Hard code solana as destination domain assert_eq!(destination_cctp_domain, 5); let message_transmitter_config_data = test_context .banks_client - .get_account(*message_transmitter_config_pubkey) + .get_account(message_transmitter_config_pubkey) .await .expect("Failed to fetch account") .expect("Account not found") diff --git a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs index 32e7478ae..b35098162 100644 --- a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs +++ b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs @@ -5,7 +5,7 @@ use solana_sdk::{keccak, pubkey::Pubkey}; -use super::Chain; +use super::{Chain, REGISTERED_TOKEN_ROUTERS}; pub trait ToBytes { fn to_bytes(&self) -> [u8; 32]; @@ -144,4 +144,22 @@ impl ChainAddress { address: TestPubkey::Bytes(address), } } + + pub fn from_registered_token_router(chain: Chain) -> Self { + match chain { + Chain::Arbitrum => Self::new_with_address( + chain, + REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), + ), + Chain::Ethereum => Self::new_with_address( + chain, + REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), + ), + Chain::Solana => Self::new_with_address( + chain, + REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), + ), + _ => panic!("Unsupported chain"), + } + } } diff --git a/solana/modules/matching-engine-testing/tests/utils/vaa.rs b/solana/modules/matching-engine-testing/tests/utils/vaa.rs index 580682c36..58d099c61 100644 --- a/solana/modules/matching-engine-testing/tests/utils/vaa.rs +++ b/solana/modules/matching-engine-testing/tests/utils/vaa.rs @@ -6,7 +6,7 @@ use anchor_lang::prelude::*; use common::messages::wormhole_io::{TypePrefixedPayload, WriteableBytes}; use common::messages::{FastMarketOrder, SlowOrderResponse}; -use common::wormhole_cctp_solana::messages::Deposit; // Implements to_vec() under PrefixedPayload +use common::wormhole_cctp_solana::messages::Deposit; use secp256k1::SecretKey as SecpSecretKey; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; @@ -522,6 +522,26 @@ impl TestVaaPair { deadline } } + + pub fn get_finalized_vaa_data(&self) -> PostedVaaData { + let deposit_payload = self + .deposit_vaa + .get_payload_deserialized() + .unwrap() + .get_deposit() + .unwrap(); + PostedVaaData { + consistency_level: self.deposit_vaa.vaa_data.consistency_level, // This is arbitrary, does not matter for the test + vaa_time: self.fast_transfer_vaa.vaa_data.vaa_time, + vaa_signature_account: self.deposit_vaa.vaa_data.vaa_signature_account, + submission_time: self.fast_transfer_vaa.vaa_data.submission_time, + nonce: self.fast_transfer_vaa.vaa_data.nonce, + sequence: self.fast_transfer_vaa.vaa_data.sequence.saturating_sub(1), + emitter_chain: self.deposit_vaa.vaa_data.emitter_chain, + emitter_address: self.deposit_vaa.vaa_data.emitter_address, + payload: deposit_payload.to_vec(), + } + } } /// Creates a deposit message @@ -673,7 +693,7 @@ impl TestVaaPairs { Self(Vec::new()) } - /// Add a fast transfer to the test, the sequence number and cctp nonce are equal to the index of the test fast transfer + /// Add a vaa pair to the test, the sequence number and cctp nonce are equal to the index of the test vaa pair /// /// # Arguments /// @@ -683,7 +703,7 @@ impl TestVaaPairs { /// * `destination_address` - The destination address /// * `cctp_mint_recipient` - The CCTP mint recipient /// * `vaa_args` - The arguments for the test VAA - pub fn add_ft( + pub fn add_vaa_pair( &mut self, token_mint: Pubkey, source_address: ChainAddress, @@ -711,7 +731,7 @@ impl TestVaaPairs { is_posted, }; - let test_fast_transfer = TestVaaPair::new( + let test_vaa_pair = TestVaaPair::new( token_mint, source_address, refund_address, @@ -720,7 +740,7 @@ impl TestVaaPairs { create_deposit_and_fast_transfer_params, &test_vaa_args, ); - self.0.push(test_fast_transfer); + self.0.push(test_vaa_pair); } /// Creates a collection of test VAA pairs with a chain and address @@ -742,16 +762,26 @@ impl TestVaaPairs { destination_chain_and_address: ChainAndAddress, vaa_args: &VaaArgs, ) { - let source_address = ChainAddress::new_with_address( - source_chain_and_address.chain, - source_chain_and_address.address, - ); - let destination_address = ChainAddress::new_with_address( - destination_chain_and_address.chain, - destination_chain_and_address.address, - ); + let source_address = vaa_args + .override_emitter_chain_and_address + .clone() + .unwrap_or_else(|| { + ChainAddress::new_with_address( + source_chain_and_address.chain, + source_chain_and_address.address, + ) + }); + let destination_address = vaa_args + .override_destination_chain_and_address + .clone() + .unwrap_or_else(|| { + ChainAddress::new_with_address( + destination_chain_and_address.chain, + destination_chain_and_address.address, + ) + }); let refund_address = source_address.clone(); - self.add_ft( + self.add_vaa_pair( mint_address, source_address, refund_address, @@ -793,6 +823,8 @@ pub struct VaaArgs { pub start_timestamp: Option, pub post_vaa: bool, pub create_deposit_and_fast_transfer_params: CreateDepositAndFastTransferParams, + pub override_emitter_chain_and_address: Option, + pub override_destination_chain_and_address: Option, } pub struct ChainAndAddress { diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index b07570e2f..a9ffc2aba 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -273,6 +273,11 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let mut active_auction = Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; + require!( + fast_market_order_digest == active_auction.vaa_hash, + MatchingEngineError::VaaMismatch + ); + // Correct way to use create_program_address with existing seeds and bump let active_auction_pda = Pubkey::create_program_address( &[ @@ -353,16 +358,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }); }; - if executor_token_account.key() != active_auction.info.as_ref().unwrap().best_offer_token { - msg!("Executor token is not equal to best offer token"); - return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - executor_token_account.key(), - active_auction.info.as_ref().unwrap().best_offer_token, - )) - }); - }; - // Check initial offer token address if initial_offer_token_account.key() != active_auction.info.as_ref().unwrap().initial_offer_token diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index b236f6b96..57b7947bd 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -9,18 +9,21 @@ use crate::state::{ Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, PreparedOrderResponse, RouterEndpoint, }; +use crate::CCTP_MINT_RECIPIENT; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use common::messages::raw::LiquidityLayerDepositMessage; -use common::messages::raw::LiquidityLayerMessage; -use common::messages::raw::SlowOrderResponse; +use common::messages::SlowOrderResponse; use common::wormhole_cctp_solana::cctp::message_transmitter_program; use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; +use common::wormhole_cctp_solana::messages::Deposit; use common::wormhole_cctp_solana::utils::CctpMessage; +use common::wormhole_io::TypePrefixedPayload; +use ruint::aliases::U256; use solana_program::instruction::Instruction; use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; use solana_program::program_pack::Pack; +use wormhole_io::WriteableBytes; use crate::error::MatchingEngineError; @@ -28,18 +31,18 @@ use crate::error::MatchingEngineError; pub struct PrepareOrderResponseCctpShimData { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, - pub finalized_vaa_message: FinalizedVaaMessage, + pub finalized_vaa_message_args: FinalizedVaaMessageArgs, } #[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] -pub struct FinalizedVaaMessage { - pub base_fee: u64, // Can also get from deposit payload - pub vaa_payload: Vec, // Can get a lot of this info from the cctp message - pub deposit_payload: Vec, // Probably dont need this since its in the vaa payload (at the end) +pub struct FinalizedVaaMessageArgs { + pub base_fee: u64, // Can also get from deposit payload + pub consistency_level: u8, pub guardian_set_bump: u8, } -impl FinalizedVaaMessage { +impl FinalizedVaaMessageArgs { + #[allow(clippy::too_many_arguments)] pub fn digest( &self, sequence: u64, @@ -48,6 +51,7 @@ impl FinalizedVaaMessage { emitter_address: [u8; 32], nonce: u32, consistency_level: u8, + deposit_vaa_payload: Deposit, ) -> [u8; 32] { let message_hash = keccak::hashv(&[ timestamp.to_be_bytes().as_ref(), @@ -56,7 +60,7 @@ impl FinalizedVaaMessage { &emitter_address, &sequence.to_be_bytes(), &[consistency_level], - self.vaa_payload.as_ref(), + deposit_vaa_payload.to_vec().as_ref(), ]); // Digest is the hash of the message keccak::hashv(&[message_hash.as_ref()]) @@ -64,27 +68,18 @@ impl FinalizedVaaMessage { .try_into() .unwrap() } - - pub fn get_slow_order_response<'a>(&'a self) -> Result> { - let liquidity_layer_message: LiquidityLayerDepositMessage<'a> = - LiquidityLayerDepositMessage::parse(&self.deposit_payload) - .map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; - let slow_order_response: SlowOrderResponse<'a> = - liquidity_layer_message.to_slow_order_response_unchecked(); - Ok(slow_order_response) - } } impl PrepareOrderResponseCctpShimData { pub fn new( encoded_cctp_message: Vec, cctp_attestation: Vec, - finalized_vaa_message: FinalizedVaaMessage, + finalized_vaa_message_args: FinalizedVaaMessageArgs, ) -> Self { Self { encoded_cctp_message, cctp_attestation, - finalized_vaa_message, + finalized_vaa_message_args, } } pub fn from_bytes(data: &[u8]) -> Option { @@ -221,34 +216,15 @@ pub fn prepare_order_response_cctp_shim( let cctp_message_transmitter_program = &accounts[22]; let guardian_set = &accounts[23]; let guardian_set_signatures = &accounts[24]; - let _verify_shim_program = &accounts[25]; + let verify_shim_program = &accounts[25]; let token_program = &accounts[26]; let system_program = &accounts[27]; let receive_message_args = data.to_receive_message_args(); - let finalized_vaa_message = data.finalized_vaa_message; + let finalized_vaa_message_args = data.finalized_vaa_message_args; - let deposit_option = LiquidityLayerMessage::parse(&finalized_vaa_message.vaa_payload) - .map_err(|_| MatchingEngineError::InvalidDeposit)?; - let deposit = deposit_option - .deposit() - .ok_or_else(|| MatchingEngineError::InvalidDepositPayloadId)?; let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; - require_eq!( - cctp_message.source_domain(), - deposit.source_cctp_domain(), - MatchingEngineError::InvalidCctpMessage - ); - require_eq!( - cctp_message.destination_domain(), - deposit.destination_cctp_domain(), - MatchingEngineError::InvalidCctpMessage - ); - require_eq!( - cctp_message.nonce(), - deposit.cctp_nonce(), - MatchingEngineError::InvalidCctpMessage - ); + // Load accounts let fast_market_order_account_data = fast_market_order.data.borrow(); let fast_market_order_zero_copy = @@ -256,23 +232,12 @@ pub fn prepare_order_response_cctp_shim( // Create pdas for addresses that need to be created // Check the prepared order response account is valid let fast_market_order_digest = fast_market_order_zero_copy.digest(); - // Construct the finalised vaa message digest data - let finalized_vaa_message_digest = { - let finalised_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; - let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); - let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; - let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; - let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; - let finalised_vaa_consistency_level = fast_market_order_zero_copy.vaa_consistency_level; - finalized_vaa_message.digest( - finalised_vaa_sequence, - finalised_vaa_timestamp, - finalised_vaa_emitter_chain, - finalised_vaa_emitter_address, - finalised_vaa_nonce, - finalised_vaa_consistency_level, - ) - }; + + require_eq!( + cctp_mint_recipient.key(), + CCTP_MINT_RECIPIENT, + MatchingEngineError::InvalidMintRecipient + ); // Check that fast market order is owned by the program require!( @@ -280,7 +245,8 @@ pub fn prepare_order_response_cctp_shim( ErrorCode::ConstraintOwner ); - let checked_custodian = + // Check that custodian deserialises correctly + let _checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; // Deserialise the to_endpoint account @@ -290,16 +256,8 @@ pub fn prepare_order_response_cctp_shim( let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..]).map(Box::new)?; - let guardian_set_bump = finalized_vaa_message.guardian_set_bump; + let guardian_set_bump = finalized_vaa_message_args.guardian_set_bump; - // Check loaded vaa is deposit message - // TODO: Fix errors to be more specific - let liquidity_layer_message = - LiquidityLayerDepositMessage::parse(&finalized_vaa_message.deposit_payload) - .map_err(|_| MatchingEngineError::InvalidDepositPayloadId)?; - let slow_order_response = liquidity_layer_message - .slow_order_response() - .ok_or_else(|| MatchingEngineError::InvalidDepositPayloadId)?; let prepared_order_response_seeds = [ PreparedOrderResponse::SEED_PREFIX, &fast_market_order_digest, @@ -319,8 +277,6 @@ pub fn prepare_order_response_cctp_shim( // Check custodian account require_eq!(custodian.owner, program_id, ErrorCode::ConstraintOwner); - require!(!checked_custodian.paused, MatchingEngineError::Paused); - // Check usdc mint require_eq!( usdc.key(), @@ -393,7 +349,7 @@ pub fn prepare_order_response_cctp_shim( ); require_eq!( - _verify_shim_program.key(), + verify_shim_program.key(), wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, MatchingEngineError::InvalidProgram ); @@ -404,6 +360,39 @@ pub fn prepare_order_response_cctp_shim( MatchingEngineError::InvalidProgram ); + // Construct the finalised vaa message digest data + let finalized_vaa_message_digest = { + let finalised_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; + let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); + let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; + let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; + let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; + let finalised_vaa_consistency_level = finalized_vaa_message_args.consistency_level; + let slow_order_response = SlowOrderResponse { + base_fee: finalized_vaa_message_args.base_fee, + }; + let deposit_vaa_payload = Deposit { + token_address: usdc.key().to_bytes(), + amount: U256::from(fast_market_order_zero_copy.amount_in), + source_cctp_domain: cctp_message.source_domain(), + destination_cctp_domain: cctp_message.destination_domain(), + cctp_nonce: cctp_message.nonce(), + burn_source: from_endpoint_account.mint_recipient, + mint_recipient: cctp_mint_recipient.key().to_bytes(), + payload: WriteableBytes::new(slow_order_response.to_vec()), + }; + + finalized_vaa_message_args.digest( + finalised_vaa_sequence, + finalised_vaa_timestamp, + finalised_vaa_emitter_chain, + finalised_vaa_emitter_address, + finalised_vaa_nonce, + finalised_vaa_consistency_level, + deposit_vaa_payload, + ) + }; + // Verify deposit message shim using verify shim program // Start verify deposit message vaa shim @@ -467,7 +456,7 @@ pub fn prepare_order_response_cctp_shim( prepared_by: signer.key(), base_fee_token: base_fee_token.key(), source_chain: fast_market_order_zero_copy.vaa_emitter_chain, - base_fee: slow_order_response.base_fee(), + base_fee: finalized_vaa_message_args.base_fee, fast_vaa_timestamp: fast_market_order_zero_copy.vaa_timestamp, amount_in: fast_market_order_zero_copy.amount_in, sender: fast_market_order_zero_copy.sender, @@ -551,6 +540,10 @@ pub fn prepare_order_response_cctp_shim( receive_message_args, )?; + msg!( + "Attempting to transfer {} from cctp mint recipient to prepared custody token", + fast_market_order_zero_copy.amount_in + ); // Finally transfer minted via CCTP to prepared custody token. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 36c8d60e7..845491276 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -107,7 +107,7 @@ impl FastMarketOrder { /// Creates an payload as expected in a fast market order vaa pub fn payload(&self) -> Vec { let mut payload = vec![]; - payload.push(11_u8); // TODO: Explain why this is 11 + payload.push(11_u8); // This is the payload id for a fast market order payload.extend_from_slice(&self.amount_in.to_be_bytes()); payload.extend_from_slice(&self.min_amount_out.to_be_bytes()); payload.extend_from_slice(&self.target_chain.to_be_bytes()); @@ -120,7 +120,6 @@ impl FastMarketOrder { payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); if self.redeemer_message_length > 0 { payload.extend_from_slice( - // uisize try from should never fail &self.redeemer_message[..usize::from(self.redeemer_message_length)], ); } diff --git a/solana/programs/matching-engine/src/state/prepared_order_response.rs b/solana/programs/matching-engine/src/state/prepared_order_response.rs index 90a5a26b6..b33222f40 100644 --- a/solana/programs/matching-engine/src/state/prepared_order_response.rs +++ b/solana/programs/matching-engine/src/state/prepared_order_response.rs @@ -12,7 +12,6 @@ pub struct PreparedOrderResponseSeeds { pub struct PreparedOrderResponseInfo { pub prepared_by: Pubkey, pub base_fee_token: Pubkey, - pub fast_vaa_timestamp: u32, pub source_chain: u16, pub base_fee: u64, From f043f7e2cbd7dc3b8b4cc2a42de6c75c43f67035 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Sat, 19 Apr 2025 21:10:13 +0100 Subject: [PATCH 060/112] added some more tests --- .../shimful/shims_prepare_order_response.rs | 11 +- .../tests/shimless/prepare_order_response.rs | 11 +- .../tests/shimless/settle_auction.rs | 26 ++- .../tests/test_scenarios/prepare_order.rs | 50 +++++ .../tests/test_scenarios/settle_auction.rs | 175 ++++++++++++++++++ .../tests/testing_engine/config.rs | 8 +- .../tests/testing_engine/engine.rs | 22 ++- .../tests/testing_engine/state.rs | 12 ++ .../tests/utils/auction.rs | 41 +++- 9 files changed, 332 insertions(+), 24 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index 5ecb328e1..a7814738d 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -62,9 +62,14 @@ impl PrepareOrderResponseShimAccountsFixture { guardian_signature_info: &GuardianSignatureInfo, ) -> Self { let usdc_mint_address = testing_context.get_usdc_mint_address(); - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); + let auction_accounts = config + .overwrite_auction_accounts + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_accounts() + .expect("Auction accounts not found") + }); let to_endpoint = auction_accounts.to_router_endpoint; let from_endpoint = auction_accounts.from_router_endpoint; let fast_market_order = current_state diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 10367dea6..ebe34d129 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -51,9 +51,14 @@ pub async fn prepare_order_response( current_state: &TestingEngineState, base_fee_token_address: &Pubkey, ) -> Option { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); + let auction_accounts = config + .overwrite_auction_accounts + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_accounts() + .expect("Auction accounts not found") + }); let to_endpoint_address = &auction_accounts.to_router_endpoint; let from_endpoint_address = &auction_accounts.from_router_endpoint; let payer_signer = config diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 1862110b9..68f890446 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -1,4 +1,5 @@ use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::SettleAuctionInstructionConfig; use crate::testing_engine::setup::TestingContext; use crate::testing_engine::state::OrderPreparedState; use crate::testing_engine::state::TestingEngineState; @@ -11,9 +12,8 @@ use matching_engine::accounts::SettleAuctionComplete as SettleAuctionCompleteCpi use matching_engine::instruction::SettleAuctionComplete; use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; -use solana_sdk::signature::{Keypair, Signer}; +use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; -use std::rc::Rc; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; /// Settle an auction (shimless) @@ -37,10 +37,23 @@ pub async fn settle_auction_complete( testing_context: &TestingContext, current_state: &TestingEngineState, test_context: &mut ProgramTestContext, - payer_signer: &Rc, + config: &SettleAuctionInstructionConfig, expected_error: Option<&ExpectedError>, ) -> AuctionState { - let auction_state = current_state.auction_state(); + let payer_signer = &config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let active_auction = config + .overwrite_active_auction_state + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_state() + .get_active_auction() + .expect("Failed to get active auction") + }); + let order_prepared_state = current_state .order_prepared() .expect("Order prepared not found"); @@ -52,9 +65,6 @@ pub async fn settle_auction_complete( } = *order_prepared_state; let matching_engine_program_id = testing_context.get_matching_engine_program_id(); - let active_auction = auction_state - .get_active_auction() - .expect("Failed to get active auction"); let event_seeds = EVENT_AUTHORITY_SEED; let event_authority = Pubkey::find_program_address(&[event_seeds], &matching_engine_program_id).0; @@ -94,6 +104,6 @@ pub async fn settle_auction_complete( if expected_error.is_none() { AuctionState::Settled } else { - auction_state.clone() + AuctionState::Active(Box::new(active_auction.clone())) } } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index 0ee409d26..1452611f7 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -315,6 +315,56 @@ pub async fn test_prepare_order_response_shim_after_custodian_is_paused_after_in .await; } +/// Prepare order response shim for completed auction after grace period +#[tokio::test] +pub async fn test_prepare_order_response_shim_for_completed_auction_after_grace_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + testing_engine + .make_auction_passed_grace_period(&mut test_context, &place_initial_offer_state, 1) + .await; + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + +/// Prepare order response shim for active auction +#[tokio::test] +pub async fn test_prepare_order_response_shim_within_auction_period() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + let instruction_triggers = vec![InstructionTrigger::PrepareOrderShim( + PrepareOrderResponseInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + /* Sad path tests section diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index efa0b1ed6..54fd7a437 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -14,7 +14,9 @@ use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; use crate::utils; +use crate::utils::auction::{ActiveAuctionState, AuctionAccounts}; +use anchor_lang::error::ErrorCode; use solana_program_test::tokio; use testing_engine::config::*; use testing_engine::engine::{InstructionTrigger, TestingEngine}; @@ -150,6 +152,179 @@ pub async fn test_settle_auction_balance_changes() { helpers::compare_balance_changes(&balance_changes_shim, &balance_changes_shimless); } +/// Test settle auction prepare order before active auction +#[tokio::test] +pub async fn test_settle_auction_prepare_order_before_active_auction() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vec![VaaArgs::default()]), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ]; + let create_cctp_router_endpoints_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + + // This is just needed to get the router endpoint accounts when prepare order happens before place initial offer, it is not used for anything else + let fake_auction_accounts = AuctionAccounts::fake_auction_accounts( + &create_cctp_router_endpoints_state, + &testing_engine.testing_context, + ); + let instruction_triggers = vec![ + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + overwrite_auction_accounts: Some(fake_auction_accounts), + ..Default::default() + }), + ]; + let prepared_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(create_cctp_router_endpoints_state), + ) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; +} + +/// Test settle auction with base_fee_token != best offer actor +#[tokio::test] +pub async fn test_settle_auction_base_fee_token_not_best_offer_actor() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (place_initial_offer_state, mut test_context, testing_engine) = + Box::pin(place_initial_offer_shim( + PlaceInitialOfferInstructionConfig::default(), + None, + transfer_direction, + )) + .await; + + let instruction_triggers = vec![ + InstructionTrigger::ExecuteOrderShim(ExecuteOrderInstructionConfig::default()), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + actor_enum: TestingActorEnum::Solver(2), + ..Default::default() + }), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig::default()), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(place_initial_offer_state), + ) + .await; +} + +/* + Sad path tests section + + ***************** + ****** ****** + **** **** + **** *** + *** *** + ** *** *** ** + ** ******* ******* *** + ** ******* ******* ** + ** ******* ******* ** + ** *** *** ** +** ** +** ** +** ** +** ** + ** ************ ** + ** ****** ****** ** + *** ***** ***** *** + ** *** *** ** + *** ** ** *** + **** **** + **** **** + ****** ****** + ***************** +*/ + +/// Test cannot settle non-existent auction +#[tokio::test] +pub async fn test_settle_auction_non_existent() { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vec![VaaArgs::default()]), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ]; + let create_cctp_router_endpoints_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + + let fake_auction_accounts = AuctionAccounts::fake_auction_accounts( + &create_cctp_router_endpoints_state, + &testing_engine.testing_context, + ); + let fake_active_auction_state = + ActiveAuctionState::fake_active_auction_state(&fake_auction_accounts); + let instruction_triggers = vec![ + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + overwrite_auction_accounts: Some(fake_auction_accounts), + ..Default::default() + }), + InstructionTrigger::SettleAuction(SettleAuctionInstructionConfig { + overwrite_active_auction_state: Some(fake_active_auction_state), + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: u32::from(ErrorCode::AccountNotInitialized), + error_string: "AccountNotInitialized".to_string(), + }), + ..SettleAuctionInstructionConfig::default() + }), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(create_cctp_router_endpoints_state), + ) + .await; +} + +/* +Helper code +*/ mod helpers { use super::*; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 31575be81..942b2aa33 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -22,7 +22,11 @@ use std::{ use crate::{ shimless::initialize::AuctionParametersConfig, - utils::{token_account::SplTokenEnum, Chain}, + utils::{ + auction::{ActiveAuctionState, AuctionAccounts}, + token_account::SplTokenEnum, + Chain, + }, }; use anchor_lang::prelude::*; use solana_program_test::ProgramTestContext; @@ -153,6 +157,7 @@ impl InstructionConfig for SetPauseCustodianInstructionConfig { #[derive(Clone, Default)] pub struct PrepareOrderResponseInstructionConfig { pub fast_market_order_address: OverwriteCurrentState, + pub overwrite_auction_accounts: OverwriteCurrentState, pub actor_enum: TestingActorEnum, pub token_enum: SplTokenEnum, pub vaa_index: usize, @@ -208,6 +213,7 @@ impl InstructionConfig for ExecuteOrderInstructionConfig { #[derive(Clone, Default)] pub struct SettleAuctionInstructionConfig { + pub overwrite_active_auction_state: OverwriteCurrentState, pub payer_signer: Option>, pub expected_error: Option, pub expected_log_messages: Option>, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index d04304da7..2f1ad38fb 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -532,6 +532,7 @@ impl TestingEngine { }, auction_state: current_state.auction_state().clone(), auction_accounts: current_state.auction_accounts().cloned(), + order_prepared: current_state.order_prepared().cloned(), } } else { current_state.clone() @@ -638,6 +639,7 @@ impl TestingEngine { fast_market_order: current_state.fast_market_order().cloned(), auction_state, auction_accounts, + order_prepared: current_state.order_prepared().cloned(), }; } current_state.clone() @@ -674,6 +676,7 @@ impl TestingEngine { fast_market_order: current_state.fast_market_order().cloned(), auction_state: new_auction_state, auction_accounts: current_state.auction_accounts().cloned(), + order_prepared: current_state.order_prepared().cloned(), }; } current_state.clone() @@ -713,6 +716,7 @@ impl TestingEngine { fast_market_order: current_state.fast_market_order().cloned(), auction_state: initial_offer_placed_state.auction_state, auction_accounts, + order_prepared: current_state.order_prepared().cloned(), }; } current_state.clone() @@ -837,9 +841,15 @@ impl TestingEngine { ) .await; if config.expected_error.is_none() { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); + let auction_accounts = + config + .overwrite_auction_accounts + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_accounts() + .expect("Auction accounts not found") + }); let prepare_order_response_fixture = result.unwrap(); let order_prepared_state = OrderPreparedState { prepared_order_response_address: prepare_order_response_fixture @@ -916,15 +926,11 @@ impl TestingEngine { current_state: &TestingEngineState, config: &SettleAuctionInstructionConfig, ) -> TestingEngineState { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); let auction_state = shimless::settle_auction::settle_auction_complete( &self.testing_context, current_state, test_context, - &payer_signer, + config, config.expected_error(), ) .await; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 99c9f2e21..5aee594bc 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -117,6 +117,7 @@ pub enum TestingEngineState { guardian_set_state: GuardianSetState, auction_state: AuctionState, auction_accounts: Option, + order_prepared: Option, }, InitialOfferPlaced { base: BaseState, @@ -125,6 +126,7 @@ pub enum TestingEngineState { fast_market_order: Option, auction_state: AuctionState, auction_accounts: AuctionAccounts, + order_prepared: Option, }, OfferImproved { base: BaseState, @@ -133,6 +135,7 @@ pub enum TestingEngineState { fast_market_order: Option, auction_state: AuctionState, auction_accounts: Option, + order_prepared: Option, }, OrderExecuted { base: BaseState, @@ -331,6 +334,9 @@ impl TestingEngineState { Self::OrderPrepared { order_prepared, .. } => Some(order_prepared), Self::AuctionSettled { order_prepared, .. } => Some(order_prepared), Self::FastMarketOrderClosed { order_prepared, .. } => order_prepared.as_ref(), + Self::InitialOfferPlaced { order_prepared, .. } => order_prepared.as_ref(), + Self::OfferImproved { order_prepared, .. } => order_prepared.as_ref(), + Self::FastMarketOrderAccountCreated { order_prepared, .. } => order_prepared.as_ref(), _ => None, } } @@ -376,6 +382,7 @@ impl TestingEngineState { guardian_set_state, auction_state: _, // Ignore the current auction state auction_accounts, + order_prepared, } => Ok(Self::FastMarketOrderAccountCreated { base: base.clone(), initialized: initialized.clone(), @@ -384,6 +391,7 @@ impl TestingEngineState { guardian_set_state: guardian_set_state.clone(), auction_state: new_auction_state, // Use the new auction state auction_accounts: auction_accounts.clone(), + order_prepared: order_prepared.clone(), }), Self::InitialOfferPlaced { @@ -393,6 +401,7 @@ impl TestingEngineState { fast_market_order, auction_state: _, // Ignore the current auction state auction_accounts, + order_prepared, } => Ok(Self::InitialOfferPlaced { base: base.clone(), initialized: initialized.clone(), @@ -400,6 +409,7 @@ impl TestingEngineState { fast_market_order: fast_market_order.clone(), auction_state: new_auction_state, // Use the new auction state auction_accounts: auction_accounts.clone(), + order_prepared: order_prepared.clone(), }), Self::OfferImproved { @@ -409,6 +419,7 @@ impl TestingEngineState { fast_market_order, auction_state: _, // Ignore the current auction state auction_accounts, + order_prepared, } => Ok(Self::OfferImproved { base: base.clone(), initialized: initialized.clone(), @@ -416,6 +427,7 @@ impl TestingEngineState { fast_market_order: fast_market_order.clone(), auction_state: new_auction_state, // Use the new auction state auction_accounts: auction_accounts.clone(), + order_prepared: order_prepared.clone(), }), Self::OrderExecuted { diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 02b880992..abd5c4b60 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -1,11 +1,13 @@ use anchor_lang::prelude::*; use anchor_spl::token::TokenAccount; +use matching_engine::ID; use solana_program_test::ProgramTestContext; use super::Chain; use super::{router::TestRouterEndpoints, token_account::SplTokenEnum}; use crate::testing_engine::config::TestingActorEnum; use crate::testing_engine::setup::{TestingActor, TestingContext, TransferDirection}; +use crate::testing_engine::state::TestingEngineState; use anyhow::{anyhow, ensure, Result as AnyhowResult}; use matching_engine::state::{Auction, AuctionConfig, AuctionInfo}; @@ -124,6 +126,18 @@ pub struct AuctionCalculations { impl ActiveAuctionState { pub const BPS_DENOMINATOR: u64 = 1_000_000; + + pub fn fake_active_auction_state(auction_accounts: &AuctionAccounts) -> Self { + Self { + auction_address: Pubkey::new_unique(), + auction_custody_token_address: Pubkey::new_unique(), + auction_config_address: auction_accounts.auction_config, + initial_offer: AuctionOffer::default(), + best_offer: AuctionOffer::default(), + spl_token_enum: auction_accounts.spl_token_enum.clone(), + } + } + /// Computes the penalty amount and user reward for the auction /// /// # Arguments @@ -410,7 +424,7 @@ impl ActiveAuctionState { /// * `participant` - The participant of the offer /// * `offer_token` - The token of the offer /// * `offer_price` - The price of the offer -#[derive(Clone)] +#[derive(Clone, Default)] pub struct AuctionOffer { pub actor: TestingActorEnum, pub participant: Pubkey, @@ -419,6 +433,31 @@ pub struct AuctionOffer { } impl AuctionAccounts { + pub fn fake_auction_accounts( + current_state: &TestingEngineState, + testing_context: &TestingContext, + ) -> Self { + let router_endpoints = current_state + .router_endpoints() + .clone() + .unwrap() + .endpoints + .clone(); + let actor = testing_context.testing_actors.owner.clone(); + let transfer_direction = testing_context.transfer_direction; + let auction_config = Pubkey::find_program_address(&[AuctionConfig::SEED_PREFIX], &ID).0; + Self::new( + None, + actor, + None, + auction_config, + &router_endpoints, + Pubkey::new_unique(), + SplTokenEnum::Usdc, + transfer_direction, + ) + } + #[allow(clippy::too_many_arguments)] pub fn new( posted_fast_vaa: Option, From a2a3ebad1cdf9928a72262333ca278b8810db984 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 16:35:36 +0100 Subject: [PATCH 061/112] removed src/lib from testing module --- solana/modules/matching-engine-testing/src/lib.rs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 solana/modules/matching-engine-testing/src/lib.rs diff --git a/solana/modules/matching-engine-testing/src/lib.rs b/solana/modules/matching-engine-testing/src/lib.rs deleted file mode 100644 index 91cfe393b..000000000 --- a/solana/modules/matching-engine-testing/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left.saturating_add(right) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From 6b3c44850c285d5094b12f789fb7c6a879a48b0f Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 16:36:23 +0100 Subject: [PATCH 062/112] changed name of lup.json --- .../matching-engine-testing/tests/fixtures/{lup.json => lut.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename solana/modules/matching-engine-testing/tests/fixtures/{lup.json => lut.json} (100%) diff --git a/solana/modules/matching-engine-testing/tests/fixtures/lup.json b/solana/modules/matching-engine-testing/tests/fixtures/lut.json similarity index 100% rename from solana/modules/matching-engine-testing/tests/fixtures/lup.json rename to solana/modules/matching-engine-testing/tests/fixtures/lut.json From 6f30c6741bdb299e35849c04cec3b30ec56c57f2 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 16:50:13 +0100 Subject: [PATCH 063/112] address shimful directory comments. Added ability to create transaction with compute unit price and compute unit limit --- .../tests/shimful/README.md | 3 +-- .../tests/shimful/fast_market_order_shim.rs | 22 +++++++++---------- .../tests/testing_engine/setup.rs | 22 +++++++++++++++++++ .../tests/utils/account_fixtures.rs | 2 +- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/README.md b/solana/modules/matching-engine-testing/tests/shimful/README.md index f0ed5f00b..580799c32 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/README.md +++ b/solana/modules/matching-engine-testing/tests/shimful/README.md @@ -1,6 +1,6 @@ # Shimful Tests -This directory contains tests that use the fallback program. +This directory contains tests that use the fallback entrypoint of the matching engine program. ## Files @@ -8,4 +8,3 @@ This directory contains tests that use the fallback program. - `shims_make_offer.rs` - A function that places an initial offer and one that improves an offer - `shims_execute_order.rs` - A function that executes an order - `shims_prepare_order_response.rs` - A function that prepares an order response -- `shims_settle_auction.rs` - A function that settles an auction \ No newline at end of file diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index f5333de20..890e0b845 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -54,19 +54,17 @@ pub async fn initialise_fast_market_order_fallback( fast_market_order, guardian_signature_info, ); - let recent_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .unwrap(); - let transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( - &[initialise_fast_market_order_ix], - Some(&payer_signer.pubkey()), - &[payer_signer], - recent_blockhash, - ); - let versioned_transaction = VersionedTransaction::from(transaction); + let transaction = testing_context + .create_transaction( + &[initialise_fast_market_order_ix], + Some(&payer_signer.pubkey()), + &[payer_signer], + 1000000000, + 1000000000, + ) + .await; testing_context - .execute_and_verify_transaction(test_context, versioned_transaction, expected_error) + .execute_and_verify_transaction(test_context, transaction, expected_error) .await; } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 569d70c43..4ae7d1417 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -38,6 +38,7 @@ use anyhow::Result as AnyhowResult; use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::{BanksClientError, ProgramTest, ProgramTestContext}; use solana_sdk::clock::Clock; +use solana_sdk::compute_budget::ComputeBudgetInstruction; use solana_sdk::instruction::InstructionError; use solana_sdk::transaction::{TransactionError, VersionedTransaction}; use solana_sdk::{ @@ -377,6 +378,27 @@ impl TestingContext { Ok(()) } + pub async fn create_transaction( + &self, + instructions: &[Instruction], + payer: Option<&Pubkey>, + signers: &[&Keypair], + compute_unit_price: u64, + compute_unit_limit: u64, + ) -> VersionedTransaction { + let last_blockhash = self.get_new_latest_blockhash(test_context).await; + let compute_budget_price = + ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price); + let compute_budget_limit = + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit); + let instructions = [ + &compute_budget_price, + &compute_budget_limit, + instructions.to_vec(), + ]; + Transaction::new_signed_with_payer(instructions, payer, signers, last_blockhash) + } + // TODO: Edit to handle multiple instructions in a single transaction pub async fn execute_and_verify_transaction( &self, diff --git a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs index d1a19f181..6df092dc5 100644 --- a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs @@ -51,7 +51,7 @@ impl FixtureAccounts { core_guardian_set: add_account_from_file(program_test, "tests/fixtures/accounts/core_bridge/guardian_set_0.json").address, message_transmitter_config: add_account_from_file(program_test, "tests/fixtures/accounts/message_transmitter/message_transmitter_config.json").address, // matching_engine_custodian: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/matching_engine_custodian.json").address, - matching_engine_custodian: pubkey!("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr"), + matching_engine_custodian: Pubkey::from_str("5BsCKkzuZXLygduw6RorCqEB61AdzNkxp5VzQrFGzYWr").unwrap(), token_router_custodian: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/token_router_custodian.json").address, token_router_program: add_account_from_file(program_test, "tests/fixtures/accounts/testnet/token_router_program_data_hacked.json").address, arbitrum_remote_token_messenger: add_account_from_file(program_test, "tests/fixtures/accounts/token_messenger_minter/arbitrum_remote_token_messenger.json").address, From f6f929d9e0153b0919885d002983b7b8e9220ce9 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 17:02:54 +0100 Subject: [PATCH 064/112] burn_and_post.rs + execute_order.rs comments addressed --- .../src/fallback/processor/burn_and_post.rs | 22 +---------- .../src/fallback/processor/execute_order.rs | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index dac2e64e8..ffa83ebd7 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -7,36 +7,18 @@ use common::wormhole_cctp_solana::{ cpi::BurnAndPublishArgs, }; use solana_program::program::invoke_signed_unchecked; +use wormhole_svm_definitions::solana::Finality; use wormhole_svm_definitions::solana::{ CORE_BRIDGE_CONFIG, CORE_BRIDGE_FEE_COLLECTOR, CORE_BRIDGE_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID, }; -use wormhole_svm_definitions::{ - find_emitter_sequence_address, find_shim_message_address, solana::Finality, -}; use wormhole_svm_shim::post_message; // This is a helper struct to make it easier to pass in the accounts for the post_message instruction. pub struct PostMessageAccounts { pub emitter: Pubkey, pub payer: Pubkey, - derived: PostMessageDerivedAccounts, -} - -impl PostMessageAccounts { - pub fn new(emitter: Pubkey, payer: Pubkey) -> Self { - Self { - emitter, - payer, - derived: Self::get_derived_accounts(&emitter), - } - } - fn get_derived_accounts(emitter: &Pubkey) -> PostMessageDerivedAccounts { - PostMessageDerivedAccounts { - message: find_shim_message_address(emitter, &POST_MESSAGE_SHIM_PROGRAM_ID).0, - sequence: find_emitter_sequence_address(emitter, &CORE_BRIDGE_PROGRAM_ID).0, - } - } + pub derived: PostMessageDerivedAccounts, } pub struct PostMessageDerivedAccounts { diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index a9ffc2aba..023c7a02f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,10 +1,11 @@ use super::helpers::check_account_length; +use crate::fallback::burn_and_post::PostMessageDerivedAccounts; use crate::state::{ Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, }; -use crate::utils; use crate::utils::auction::DepositPenalty; +use crate::{utils, ID}; use anchor_lang::prelude::*; use anchor_spl::token::{spl_token, TokenAccount}; use common::messages::Fill; @@ -35,7 +36,7 @@ pub struct ExecuteOrderShimAccounts<'ix> { pub active_auction_config: &'ix Pubkey, // 6 /// The token account of the auction's best offer pub active_auction_best_offer_token: &'ix Pubkey, // 7 - /// ??? + /// The token account of the executor pub executor_token: &'ix Pubkey, // 8 /// The token account of the auction's initial offer pub initial_offer_token: &'ix Pubkey, // 9 @@ -45,10 +46,10 @@ pub struct ExecuteOrderShimAccounts<'ix> { pub to_router_endpoint: &'ix Pubkey, // 11 /// The program id of the post message shim program pub post_message_shim_program: &'ix Pubkey, // 12 - /// The sequence account of the post message shim program (can be derived) - pub post_message_sequence: &'ix Pubkey, // 13 + /// The emitter sequence of the core bridge program (can be derived) + pub core_bridge_emitter_sequence: &'ix Pubkey, // 13 /// The message account of the post message shim program (can be derived) - pub post_message_message: &'ix Pubkey, // 14 + pub post_shim_message: &'ix Pubkey, // 14 /// The mint account of the CCTP token to be burned pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 15 /// The token messenger minter sender authority account of the CCTP token to be burned @@ -101,8 +102,8 @@ impl<'ix> ExecuteOrderShimAccounts<'ix> { AccountMeta::new(*self.initial_participant, false), AccountMeta::new_readonly(*self.to_router_endpoint, false), AccountMeta::new_readonly(*self.post_message_shim_program, false), - AccountMeta::new(*self.post_message_sequence, false), - AccountMeta::new(*self.post_message_message, false), + AccountMeta::new(*self.core_bridge_emitter_sequence, false), + AccountMeta::new(*self.post_shim_message, false), AccountMeta::new(*self.cctp_deposit_for_burn_mint, false), AccountMeta::new_readonly( *self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, @@ -158,7 +159,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not check_account_length(accounts, 31)?; - let program_id = &crate::ID; + let program_id = &ID; // Get the accounts let signer_account = &accounts[0]; @@ -174,8 +175,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { let initial_participant_account = &accounts[10]; let to_router_endpoint_account = &accounts[11]; let _post_message_shim_program_account = &accounts[12]; - let _post_message_sequence_account = &accounts[13]; - let _post_message_message_account = &accounts[14]; + let core_bridge_emitter_sequence_account = &accounts[13]; + let post_shim_message_account = &accounts[14]; let cctp_deposit_for_burn_mint_account = &accounts[15]; let cctp_deposit_for_burn_token_messenger_minter_sender_authority_account = &accounts[16]; let cctp_deposit_for_burn_message_transmitter_config_account = &accounts[17]; @@ -441,12 +442,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // the fast fill will most likely require an additional transaction, so this buffer allows // the best offer participant to perform his duty without the risk of getting slashed by // another executor. - let additional_grace_period = match active_auction.target_protocol { - MessageProtocol::Local { .. } => { - crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD.into() - } - _ => None, - }; + let additional_grace_period = Some(crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD); let DepositPenalty { penalty, @@ -634,9 +630,14 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .unwrap(), }; - let post_message_accounts = - PostMessageAccounts::new(custodian_account.key(), signer_account.key()); - // Lets print the auction account balance + let post_message_accounts = PostMessageAccounts { + emitter: custodian_account.key(), + payer: signer_account.key(), + derived: PostMessageDerivedAccounts { + message: post_shim_message_account.key(), + sequence: core_bridge_emitter_sequence_account.key(), + }, + }; burn_and_post( CpiContext::new_with_signer( From f4d6be46576ff9a3382f1fe16f4cb19f183dcef5 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 17:07:02 +0100 Subject: [PATCH 065/112] close_fast_market_order.rs ammended --- .../src/fallback/processor/close_fast_market_order.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index d57b0df66..56aad9eb1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -59,8 +59,8 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { msg!("Refund recipient (account #2) is not a signer"); return Err(ProgramError::InvalidAccountData.into()); } - let fast_market_order_deserialized = - FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; + let fast_market_order_data = &fast_market_order.data.borrow()[..]; + let fast_market_order_deserialized = FastMarketOrder::try_read(fast_market_order_data)?; // Check that the fast_market_order is owned by the close_account_refund_recipient if fast_market_order_deserialized.close_account_refund_recipient != close_account_refund_recipient.key().as_ref() @@ -75,15 +75,13 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { ); } - let fast_market_order_data = - FastMarketOrder::try_deserialize(&mut &fast_market_order.data.borrow()[..])?; - if fast_market_order_data.close_account_refund_recipient + if fast_market_order_deserialized.close_account_refund_recipient != close_account_refund_recipient.key().as_ref() { return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( |e: Error| { e.with_pubkeys(( - Pubkey::from(fast_market_order_data.close_account_refund_recipient), + Pubkey::from(fast_market_order_deserialized.close_account_refund_recipient), close_account_refund_recipient.key(), )) }, From 15cef8b1513d54be1af31c3fd68c48a1df11e99e Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 17:22:21 +0100 Subject: [PATCH 066/112] lut and compute limit mistake ammended --- .../tests/shimful/fast_market_order_shim.rs | 2 +- .../tests/shimful/shims_execute_order.rs | 4 ++-- .../tests/shimful/shims_make_offer.rs | 2 +- .../tests/testing_engine/setup.rs | 20 +++++++++---------- .../tests/utils/account_fixtures.rs | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 890e0b845..c9a86fb9e 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -17,7 +17,6 @@ use utils::constants::*; use matching_engine::state::{FastMarketOrder as FastMarketOrderState, FastMarketOrderParams}; use solana_program_test::ProgramTestContext; -use solana_sdk::transaction::VersionedTransaction; use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}; use std::rc::Rc; use wormhole_io::TypePrefixedPayload; @@ -56,6 +55,7 @@ pub async fn initialise_fast_market_order_fallback( ); let transaction = testing_context .create_transaction( + test_context, &[initialise_fast_market_order_ix], Some(&payer_signer.pubkey()), &[payer_signer], diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 030a7ef6c..a62878edb 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -235,8 +235,8 @@ pub fn create_execute_order_shim_accounts<'ix>( initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 - post_message_sequence: &execute_order_fallback_fixture.post_message_sequence, // 13 - post_message_message: &execute_order_fallback_fixture.post_message_message, // 14 + core_bridge_emitter_sequence: &execute_order_fallback_fixture.post_message_sequence, // 13 + post_shim_message: &execute_order_fallback_fixture.post_message_message, // 14 cctp_deposit_for_burn_mint: &USDC_MINT, // 15 cctp_deposit_for_burn_token_messenger_minter_sender_authority: &execute_order_fallback_fixture diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 5c091a3c5..02dfaa23d 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -124,7 +124,7 @@ pub async fn place_initial_offer_fallback( .get_token_account_balance(test_context, &config.spl_token_enum) .await; - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData::new(offer_price); + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData { offer_price }; let (from_router_endpoint, to_router_endpoint) = config.get_from_and_to_router_endpoints(current_state); diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 4ae7d1417..7f8ba95ad 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -39,7 +39,7 @@ use matching_engine::{CCTP_MINT_RECIPIENT, ID as PROGRAM_ID}; use solana_program_test::{BanksClientError, ProgramTest, ProgramTestContext}; use solana_sdk::clock::Clock; use solana_sdk::compute_budget::ComputeBudgetInstruction; -use solana_sdk::instruction::InstructionError; +use solana_sdk::instruction::{Instruction, InstructionError}; use solana_sdk::transaction::{TransactionError, VersionedTransaction}; use solana_sdk::{ pubkey::Pubkey, @@ -380,23 +380,23 @@ impl TestingContext { pub async fn create_transaction( &self, + test_context: &mut ProgramTestContext, instructions: &[Instruction], payer: Option<&Pubkey>, signers: &[&Keypair], compute_unit_price: u64, - compute_unit_limit: u64, - ) -> VersionedTransaction { - let last_blockhash = self.get_new_latest_blockhash(test_context).await; + compute_unit_limit: u32, + ) -> Transaction { + let last_blockhash = self.get_new_latest_blockhash(test_context).await.unwrap(); let compute_budget_price = ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price); let compute_budget_limit = ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit); - let instructions = [ - &compute_budget_price, - &compute_budget_limit, - instructions.to_vec(), - ]; - Transaction::new_signed_with_payer(instructions, payer, signers, last_blockhash) + let mut all_instructions = Vec::with_capacity(instructions.len() + 2); + all_instructions.push(compute_budget_price.clone()); + all_instructions.push(compute_budget_limit.clone()); + all_instructions.extend_from_slice(instructions); + Transaction::new_signed_with_payer(&all_instructions, payer, signers, last_blockhash) } // TODO: Edit to handle multiple instructions in a single transaction diff --git a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs index 6df092dc5..c3da0ad32 100644 --- a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs @@ -4,7 +4,7 @@ //! It includes methods for creating accounts and for reading a keypair from a JSON fixture file. //! These accounts are located in the `tests/fixtures/accounts` directory. -use anchor_lang::prelude::{pubkey, Pubkey}; +use anchor_lang::prelude::Pubkey; use anyhow::Result as AnyhowResult; use serde_json::Value; use solana_program_test::ProgramTest; @@ -70,7 +70,7 @@ impl FixtureAccounts { /// /// * `program_test` - The program test instance pub fn add_lookup_table_hack(program_test: &mut ProgramTest) { - let filename = "tests/fixtures/lup.json"; + let filename = "tests/fixtures/lut.json"; let account_fixture = read_account_from_file(filename).unwrap(); program_test.add_account_with_file_data( account_fixture.address, From 265ac1434356843bd266c697d097c3ce36641845 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 19:20:14 +0100 Subject: [PATCH 067/112] place initial offer comments --- .../src/fallback/processor/execute_order.rs | 4 +- .../src/fallback/processor/helpers.rs | 49 +++++++++ .../fallback/processor/place_initial_offer.rs | 100 +++++++++--------- .../processor/prepare_order_response.rs | 4 +- 4 files changed, 104 insertions(+), 53 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 023c7a02f..1061670c3 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -197,8 +197,8 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Do checks // ------------------------------------------------------------------------------------------------ - let fast_market_order_zero_copy = - FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; + let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; + let fast_market_order_zero_copy = FastMarketOrderState::try_read(fast_market_order_data)?; // Bind value for compiler (needed for pda seeds) let active_auction_key = active_auction_account.key(); diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 41633677f..0f4252026 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,5 +1,6 @@ use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; use solana_program::{ entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, @@ -129,3 +130,51 @@ pub fn create_account_reliably( Ok(()) } + +/// Create a token account reliably +/// +/// This function creates a token account and initializes it with the given mint and owner. +/// +/// # Arguments +/// +/// * `payer_pubkey` - The pubkey of the account that will pay for the token account. +/// * `account_pubkey_to_create` - The pubkey of the account to create. +/// * `owner_account_info` - The account info of the owner of the token account. +/// * `mint_pubkey` - The pubkey of the mint. +/// * `data_len` - The length of the data to be written to the token account. +/// * `accounts` - The accounts to be used in the CPI. +/// * `signer_seeds` - The signer seeds to be used in the CPI. +#[allow(clippy::too_many_arguments)] +pub fn create_token_account_reliably( + payer_pubkey: &Pubkey, + account_pubkey_to_create: &Pubkey, + owner_account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + data_len: usize, + token_account_lamports: u64, + accounts: &[AccountInfo], + signer_seeds: &[&[&[u8]]], +) -> ProgramResult { + // Create the owner account + create_account_reliably( + payer_pubkey, + account_pubkey_to_create, + token_account_lamports, + data_len, + accounts, + &spl_token::ID, + signer_seeds, + )?; + + // Create the token account + let init_token_account_ix = spl_token::instruction::initialize_account3( + &spl_token::ID, + account_pubkey_to_create, + mint_pubkey, + owner_account_pubkey, + )?; + + solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[])?; + + Ok(()) +} diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 5636c6408..a46d28871 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,8 +1,10 @@ use super::helpers::check_account_length; use super::helpers::create_account_reliably; +use super::helpers::create_token_account_reliably; +use crate::state::MessageProtocol; use crate::state::{ Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, - FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, + FastMarketOrder as FastMarketOrderState, RouterEndpoint, }; use crate::ID as PROGRAM_ID; use anchor_lang::prelude::*; @@ -25,10 +27,6 @@ pub struct PlaceInitialOfferCctpShimData { } impl PlaceInitialOfferCctpShimData { - pub fn new(offer_price: u64) -> Self { - Self { offer_price } - } - pub fn from_bytes(data: &[u8]) -> Option<&Self> { bytemuck::try_from_bytes::(data).ok() } @@ -36,18 +34,31 @@ impl PlaceInitialOfferCctpShimData { #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct PlaceInitialOfferCctpShimAccounts<'ix> { + /// The signer account pub signer: &'ix Pubkey, + /// The transfer authority account pub transfer_authority: &'ix Pubkey, + /// The custodian account pub custodian: &'ix Pubkey, + /// The auction config account pub auction_config: &'ix Pubkey, + /// The from endpoint account pub from_endpoint: &'ix Pubkey, + /// The to endpoint account pub to_endpoint: &'ix Pubkey, - pub fast_market_order: &'ix Pubkey, // Needs initalising. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] - pub auction: &'ix Pubkey, // Needs initalising + /// The fast market order account, which will be initialised. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] + pub fast_market_order: &'ix Pubkey, + /// The auction account, which will be initialised + pub auction: &'ix Pubkey, + /// The offer token account pub offer_token: &'ix Pubkey, + /// The auction custody token account pub auction_custody_token: &'ix Pubkey, + /// The usdc token account pub usdc: &'ix Pubkey, + /// The system program account pub system_program: &'ix Pubkey, + /// The token program account pub token_program: &'ix Pubkey, } @@ -119,8 +130,7 @@ impl VaaMessageBodyHeader { } } - /// This function creates both the message body and the payload. - /// This is all done here just because it's (supposedly?) cheaper in the solana vm. + /// This function creates both the message body for the fast market order, including the payload. pub fn message_body(&self, fast_market_order: &FastMarketOrderState) -> Vec { let mut message_body = vec![]; message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); @@ -129,44 +139,48 @@ impl VaaMessageBodyHeader { message_body.extend_from_slice(&self.emitter_address); message_body.extend_from_slice(&self.sequence.to_be_bytes()); message_body.extend_from_slice(&[self.consistency_level]); - let mut payload = vec![]; - payload.push(11_u8); - payload.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.redeemer); - payload.extend_from_slice(&fast_market_order.sender); - payload.extend_from_slice(&fast_market_order.refund_address); - payload.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); - payload.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); + message_body.push(11_u8); + message_body.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer); + message_body.extend_from_slice(&fast_market_order.sender); + message_body.extend_from_slice(&fast_market_order.refund_address); + message_body.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); if fast_market_order.redeemer_message_length > 0 { - payload.extend_from_slice( + message_body.extend_from_slice( &fast_market_order.redeemer_message [..usize::from(fast_market_order.redeemer_message_length)], ); } - message_body.extend_from_slice(&payload); message_body } + /// This function creates the hash of the message body for the fast market order. + /// This is used to create the digest. pub fn message_hash(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { keccak::hashv(&[self.message_body(fast_market_order).as_ref()]) } + /// The digest is the hash of the message hash. pub fn digest(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { keccak::hashv(&[self.message_hash(fast_market_order).as_ref()]) } + /// This function returns the vaa time. pub fn vaa_time(&self) -> u32 { self.vaa_time } + /// This function returns the sequence number of the fast market order. pub fn sequence(&self) -> u64 { self.sequence } + /// This function returns the emitter chain of the fast market order. pub fn emitter_chain(&self) -> u16 { self.emitter_chain } @@ -176,8 +190,7 @@ pub fn place_initial_offer_cctp_shim( accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData, ) -> Result<()> { - // Check account owners - let program_id = &crate::ID; // Your program ID + let program_id = &PROGRAM_ID; // Your program ID // Check all accounts are valid check_account_length(accounts, 11)?; @@ -204,8 +217,8 @@ pub fn place_initial_offer_cctp_shim( .map_err(|e: Error| e.with_account_name("fast_market_order_account")); } - let fast_market_order_zero_copy = - FastMarketOrderState::try_deserialize(&mut &fast_market_order_account.data.borrow()[..])?; + let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; + let fast_market_order_zero_copy = FastMarketOrderState::try_read(fast_market_order_data)?; let vaa_time = fast_market_order_zero_copy.vaa_timestamp; let sequence = fast_market_order_zero_copy.vaa_sequence; @@ -303,16 +316,16 @@ pub fn place_initial_offer_cctp_shim( return Err(MatchingEngineError::SameEndpoint.into()); } - // Check that the to endpoint protocol is cctp or local + // Check that the to endpoint is a valid protocol match to_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), _ => return Err(MatchingEngineError::InvalidEndpoint.into()), } - // Check that the from endpoint protocol is cctp or local - match from_endpoint_account.protocol { - MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), - _ => return Err(MatchingEngineError::InvalidEndpoint.into()), + // Check that the vaa emitter address equals the from_endpoints encoded address + if from_endpoint_account.address != fast_market_order_zero_copy.vaa_emitter_address { + msg!("Vaa emitter address is not equal to the from_endpoints encoded address"); + return Err(MatchingEngineError::InvalidSourceRouter.into()); } // Check that to endpoint chain is equal to the fast_market_order target_chain @@ -349,8 +362,6 @@ pub fn place_initial_offer_cctp_shim( // Begin of initialisation of auction custody token account // ------------------------------------------------------------------------------------------------ - let auction_custody_token_space = spl_token::state::Account::LEN; - let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address( &[ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, @@ -373,25 +384,17 @@ pub fn place_initial_offer_cctp_shim( &[auction_custody_token_bump], ]; let auction_custody_token_signer_seeds = &[&auction_custody_token_seeds[..]]; - create_account_reliably( + + create_token_account_reliably( &signer.key(), &auction_custody_token_pda, + &auction_account.key(), + &usdc.key(), + spl_token::state::Account::LEN, auction_custody_token.lamports(), - auction_custody_token_space, accounts, - &spl_token::ID, auction_custody_token_signer_seeds, )?; - // Initialise the token account - let init_token_account_ix = spl_token::instruction::initialize_account3( - &spl_token::ID, - &auction_custody_token_pda, - &usdc.key(), - &auction_account.key(), - ) - .unwrap(); - - solana_program::program::invoke(&init_token_account_ix, accounts).unwrap(); // ------------------------------------------------------------------------------------------------ // End of initialisation of auction custody token account @@ -494,8 +497,7 @@ pub fn place_initial_offer_cctp_shim( &offer_price.to_be_bytes(), &[transfer_authority_bump], ]], - ) - .map_err(|_| MatchingEngineError::TokenTransferFailed)?; + )?; // ------------------------------------------------------------------------------------------------ // End of token transfer from offer token to auction custody token Ok(()) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 57b7947bd..5c7ee957d 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -226,9 +226,9 @@ pub fn prepare_order_response_cctp_shim( .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; // Load accounts - let fast_market_order_account_data = fast_market_order.data.borrow(); + let fast_market_order_account_data = &fast_market_order.data.borrow()[..]; let fast_market_order_zero_copy = - FastMarketOrderState::try_read(&fast_market_order_account_data[..])?; + FastMarketOrderState::try_read(fast_market_order_account_data)?; // Create pdas for addresses that need to be created // Check the prepared order response account is valid let fast_market_order_digest = fast_market_order_zero_copy.digest(); From 335ff84fb56a8f177522642fb2c475224eef950e Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 19:44:40 +0100 Subject: [PATCH 068/112] place initial offer and prepare order response --- solana/programs/matching-engine/src/error.rs | 1 + .../processor/prepare_order_response.rs | 69 +++++++------------ .../fallback/processor/process_instruction.rs | 2 +- 3 files changed, 25 insertions(+), 47 deletions(-) diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index e6a424264..dfd043914 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -94,6 +94,7 @@ pub enum MatchingEngineError { AccountAlreadyInitialized = 0x700, AccountNotWritable = 0x702, BorshDeserializationError = 0x704, + BorshSerializationError = 0x705, InvalidPda = 0x706, AccountDataTooSmall = 0x708, InvalidProgram = 0x70a, diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 5c7ee957d..07da7caaf 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -1,8 +1,10 @@ use std::io::Cursor; use super::helpers::create_account_reliably; +use super::place_initial_offer::VaaMessageBodyHeader; use super::FallbackMatchingEngineInstruction; use crate::fallback::helpers::check_account_length; +use crate::fallback::helpers::create_token_account_reliably; use crate::state::PreparedOrderResponseInfo; use crate::state::PreparedOrderResponseSeeds; use crate::state::{ @@ -10,6 +12,7 @@ use crate::state::{ RouterEndpoint, }; use crate::CCTP_MINT_RECIPIENT; +use crate::ID; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use common::messages::SlowOrderResponse; @@ -42,24 +45,18 @@ pub struct FinalizedVaaMessageArgs { } impl FinalizedVaaMessageArgs { - #[allow(clippy::too_many_arguments)] pub fn digest( &self, - sequence: u64, - timestamp: u32, - emitter_chain: u16, - emitter_address: [u8; 32], - nonce: u32, - consistency_level: u8, + vaa_message_body_header: VaaMessageBodyHeader, deposit_vaa_payload: Deposit, ) -> [u8; 32] { let message_hash = keccak::hashv(&[ - timestamp.to_be_bytes().as_ref(), - nonce.to_be_bytes().as_ref(), - emitter_chain.to_be_bytes().as_ref(), - &emitter_address, - &sequence.to_be_bytes(), - &[consistency_level], + vaa_message_body_header.vaa_time.to_be_bytes().as_ref(), + vaa_message_body_header.nonce.to_be_bytes().as_ref(), + vaa_message_body_header.emitter_chain.to_be_bytes().as_ref(), + &vaa_message_body_header.emitter_address, + &vaa_message_body_header.sequence.to_be_bytes(), + &[vaa_message_body_header.consistency_level], deposit_vaa_payload.to_vec().as_ref(), ]); // Digest is the hash of the message @@ -71,23 +68,9 @@ impl FinalizedVaaMessageArgs { } impl PrepareOrderResponseCctpShimData { - pub fn new( - encoded_cctp_message: Vec, - cctp_attestation: Vec, - finalized_vaa_message_args: FinalizedVaaMessageArgs, - ) -> Self { - Self { - encoded_cctp_message, - cctp_attestation, - finalized_vaa_message_args, - } - } pub fn from_bytes(data: &[u8]) -> Option { Self::try_from_slice(data).ok() } - pub fn to_bytes(&self) -> Vec { - self.try_to_vec().unwrap() - } pub fn to_receive_message_args(&self) -> ReceiveMessageArgs { let mut encoded_message = Vec::with_capacity(self.encoded_cctp_message.len()); @@ -188,7 +171,7 @@ pub fn prepare_order_response_cctp_shim( accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData, ) -> Result<()> { - let program_id = &crate::ID; + let program_id = &ID; check_account_length(accounts, 27)?; let signer = &accounts[0]; @@ -366,7 +349,6 @@ pub fn prepare_order_response_cctp_shim( let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; - let finalised_vaa_nonce = fast_market_order_zero_copy.vaa_nonce; let finalised_vaa_consistency_level = finalized_vaa_message_args.consistency_level; let slow_order_response = SlowOrderResponse { base_fee: finalized_vaa_message_args.base_fee, @@ -383,12 +365,13 @@ pub fn prepare_order_response_cctp_shim( }; finalized_vaa_message_args.digest( - finalised_vaa_sequence, - finalised_vaa_timestamp, - finalised_vaa_emitter_chain, - finalised_vaa_emitter_address, - finalised_vaa_nonce, - finalised_vaa_consistency_level, + VaaMessageBodyHeader::new( + finalised_vaa_consistency_level, + finalised_vaa_timestamp, + finalised_vaa_sequence, + finalised_vaa_emitter_chain, + finalised_vaa_emitter_address, + ), deposit_vaa_payload, ) }; @@ -488,23 +471,17 @@ pub fn prepare_order_response_cctp_shim( ]; let prepared_custody_token_signer_seeds = &[&create_prepared_custody_token_seeds[..]]; - let prepared_custody_token_account_space = spl_token::state::Account::LEN; - create_account_reliably( + create_token_account_reliably( &signer.key(), &prepared_custody_token_pda, + &prepared_order_response_pda, + &usdc.key(), + spl_token::state::Account::LEN, prepared_custody_token.lamports(), - prepared_custody_token_account_space, accounts, - &spl_token::ID, prepared_custody_token_signer_seeds, )?; - let init_token_account_ix = spl_token::instruction::initialize_account3( - &spl_token::ID, - &prepared_custody_token_pda, - &usdc.key(), - &prepared_order_response_pda, - )?; - solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[])?; + // End create prepared custody token account // ------------------------------------------------------------------------------------------------ diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 3270ed81c..ad2544c1c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -149,7 +149,7 @@ impl FallbackMatchingEngineInstruction<'_> { out } Self::PrepareOrderResponseCctpShim(data) => { - let data_slice = data.to_bytes(); + let data_slice = data.try_to_vec().unwrap(); let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length let mut out = Vec::with_capacity(total_capacity); From d07a72a56eb199eec62fb1545f511dcecf6d6d89 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 20:00:06 +0100 Subject: [PATCH 069/112] initialise fast market order comments attended --- solana/programs/matching-engine/src/error.rs | 1 - .../processor/initialise_fast_market_order.rs | 57 ++++++++----------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index dfd043914..6f1e4b83e 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -96,7 +96,6 @@ pub enum MatchingEngineError { BorshDeserializationError = 0x704, BorshSerializationError = 0x705, InvalidPda = 0x706, - AccountDataTooSmall = 0x708, InvalidProgram = 0x70a, TokenTransferFailed = 0x70c, InvalidMint = 0x70e, diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index d88232d65..7918c0820 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -1,8 +1,13 @@ use anchor_lang::prelude::*; use anchor_lang::Discriminator; +use anchor_spl::token_interface::spl_token_metadata_interface::borsh::BorshDeserialize; use bytemuck::{Pod, Zeroable}; use solana_program::instruction::Instruction; +use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; +use wormhole_svm_shim::verify_vaa::VerifyHash; +use wormhole_svm_shim::verify_vaa::VerifyHashAccounts; +use wormhole_svm_shim::verify_vaa::VerifyHashData; use super::helpers::create_account_reliably; @@ -10,6 +15,7 @@ use super::helpers::check_account_length; use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; use crate::state::FastMarketOrder as FastMarketOrderState; +use crate::ID; pub struct InitialiseFastMarketOrderAccounts<'ix> { /// The signer of the transaction @@ -33,8 +39,8 @@ impl<'ix> InitialiseFastMarketOrderAccounts<'ix> { AccountMeta::new(*self.fast_market_order_account, false), AccountMeta::new_readonly(*self.guardian_set, false), AccountMeta::new_readonly(*self.guardian_set_signatures, false), - AccountMeta::new(*self.verify_vaa_shim_program, false), - AccountMeta::new(*self.system_program, false), + AccountMeta::new_readonly(*self.verify_vaa_shim_program, false), + AccountMeta::new_readonly(*self.system_program, false), ] } } @@ -108,7 +114,7 @@ pub fn initialise_fast_market_order( ) -> Result<()> { check_account_length(accounts, 6)?; - let program_id = crate::ID; + let program_id = ID; let signer = &accounts[0]; let fast_market_order_account = &accounts[1]; @@ -123,33 +129,21 @@ pub fn initialise_fast_market_order( // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ let fast_market_order_vaa_digest = fast_market_order.digest(); - // Did not want to pass in the vaa hash here. So recreated it. - let verify_hash_data = { - let mut data = vec![]; - data.extend_from_slice( - &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, - ); - data.push(guardian_set_bump); - data.extend_from_slice(&fast_market_order_vaa_digest); - data - }; - let verify_shim_ix = Instruction { - program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, // Because program is hardcoded, the check is not needed. - accounts: vec![ - AccountMeta::new_readonly(guardian_set.key(), false), - AccountMeta::new_readonly(guardian_set_signatures.key(), false), - ], + let fast_market_order_vaa_digest_hash = + keccak::Hash::try_from_slice(&fast_market_order_vaa_digest).unwrap(); + let verify_hash_data = + VerifyHashData::new(guardian_set_bump, fast_market_order_vaa_digest_hash); + let verify_hash_shim_ix = VerifyHash { + program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: VerifyHashAccounts { + guardian_set: &guardian_set.key(), + guardian_signatures: &guardian_set_signatures.key(), + }, data: verify_hash_data, - }; + } + .instruction(); // Make the cpi call to verify the shim. - invoke_signed_unchecked( - &verify_shim_ix, - &[ - guardian_set.to_account_info(), - guardian_set_signatures.to_account_info(), - ], - &[], - )?; + invoke_signed_unchecked(&verify_hash_shim_ix, accounts, &[])?; // ------------------------------------------------------------------------------------------------ // End of cpi call to verify the shim. @@ -196,12 +190,7 @@ pub fn initialise_fast_market_order( fast_market_order_account_data[0..8].copy_from_slice(&discriminator); let fast_market_order_bytes = bytemuck::bytes_of(&data.fast_market_order); - // Ensure the destination has enough space - if fast_market_order_account_data.len() < 8_usize.saturating_add(fast_market_order_bytes.len()) - { - msg!("Account data buffer too small"); - return Err(MatchingEngineError::AccountDataTooSmall.into()); - } + // Write the fast_market_order struct to the account fast_market_order_account_data[8..8_usize.saturating_add(fast_market_order_bytes.len())] .copy_from_slice(fast_market_order_bytes); From ec63679442f4df13608dff785795c3275d8b1983 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 20:04:38 +0100 Subject: [PATCH 070/112] helpers comment attended --- .../src/fallback/processor/close_fast_market_order.rs | 4 ++-- .../matching-engine/src/fallback/processor/execute_order.rs | 4 ++-- .../matching-engine/src/fallback/processor/helpers.rs | 2 +- .../src/fallback/processor/initialise_fast_market_order.rs | 4 ++-- .../src/fallback/processor/place_initial_offer.rs | 4 ++-- .../src/fallback/processor/prepare_order_response.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 56aad9eb1..02b2aab02 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -4,7 +4,7 @@ use anchor_lang::prelude::*; use solana_program::instruction::Instruction; use solana_program::program_error::ProgramError; -use super::helpers::check_account_length; +use super::helpers::require_min_account_infos_len; use super::FallbackMatchingEngineInstruction; pub struct CloseFastMarketOrderAccounts<'ix> { @@ -49,7 +49,7 @@ impl CloseFastMarketOrder<'_> { /// /// Result<()> pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { - check_account_length(accounts, 2)?; + require_min_account_infos_len(accounts, 2)?; let fast_market_order = &accounts[0]; let close_account_refund_recipient = &accounts[1]; diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 1061670c3..d86ba5a22 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,4 +1,4 @@ -use super::helpers::check_account_length; +use super::helpers::require_min_account_infos_len; use crate::fallback::burn_and_post::PostMessageDerivedAccounts; use crate::state::{ Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, @@ -157,7 +157,7 @@ impl ExecuteOrderCctpShim<'_> { pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not - check_account_length(accounts, 31)?; + require_min_account_infos_len(accounts, 31)?; let program_id = &ID; diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 0f4252026..5b2ada6b0 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -9,7 +9,7 @@ use solana_program::{ }; #[inline(always)] -pub fn check_account_length(accounts: &[AccountInfo], len: usize) -> Result<()> { +pub fn require_min_account_infos_len(accounts: &[AccountInfo], len: usize) -> Result<()> { if accounts.len() < len { return Err(ErrorCode::AccountNotEnoughKeys.into()); } diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 7918c0820..65160f7bc 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -11,7 +11,7 @@ use wormhole_svm_shim::verify_vaa::VerifyHashData; use super::helpers::create_account_reliably; -use super::helpers::check_account_length; +use super::helpers::require_min_account_infos_len; use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; use crate::state::FastMarketOrder as FastMarketOrderState; @@ -112,7 +112,7 @@ pub fn initialise_fast_market_order( accounts: &[AccountInfo], data: &InitialiseFastMarketOrderData, ) -> Result<()> { - check_account_length(accounts, 6)?; + require_min_account_infos_len(accounts, 6)?; let program_id = ID; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index a46d28871..528acba1b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,6 +1,6 @@ -use super::helpers::check_account_length; use super::helpers::create_account_reliably; use super::helpers::create_token_account_reliably; +use super::helpers::require_min_account_infos_len; use crate::state::MessageProtocol; use crate::state::{ Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, @@ -193,7 +193,7 @@ pub fn place_initial_offer_cctp_shim( let program_id = &PROGRAM_ID; // Your program ID // Check all accounts are valid - check_account_length(accounts, 11)?; + require_min_account_infos_len(accounts, 11)?; // Extract data fields let PlaceInitialOfferCctpShimData { offer_price } = *data; diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 07da7caaf..7eba0bc64 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -3,8 +3,8 @@ use std::io::Cursor; use super::helpers::create_account_reliably; use super::place_initial_offer::VaaMessageBodyHeader; use super::FallbackMatchingEngineInstruction; -use crate::fallback::helpers::check_account_length; use crate::fallback::helpers::create_token_account_reliably; +use crate::fallback::helpers::require_min_account_infos_len; use crate::state::PreparedOrderResponseInfo; use crate::state::PreparedOrderResponseSeeds; use crate::state::{ @@ -172,7 +172,7 @@ pub fn prepare_order_response_cctp_shim( data: PrepareOrderResponseCctpShimData, ) -> Result<()> { let program_id = &ID; - check_account_length(accounts, 27)?; + require_min_account_infos_len(accounts, 27)?; let signer = &accounts[0]; let custodian = &accounts[1]; From a3e17d7f19a6256ccea4dbf486e73ec315839d45 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 22 Apr 2025 22:58:31 +0100 Subject: [PATCH 071/112] attended all comments so far --- .../matching-engine-testing/tests/README.md | 9 ++++----- .../tests/utils/constants.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/README.md b/solana/modules/matching-engine-testing/tests/README.md index 6d750d772..3ea1a8920 100644 --- a/solana/modules/matching-engine-testing/tests/README.md +++ b/solana/modules/matching-engine-testing/tests/README.md @@ -32,24 +32,23 @@ What is expected: What is expected: - Fast market order account is created -- Guardian set is created +- Guardian signatures account is created via Verify VAA Shim program, which are the signatures found in the fast market order VAA from the source network. - Fast market order is initialised ### Close fast market order What is expected: - Fast market order account is closed -- Guardian set is closed -- Close account refund recipient is sent usdc +- Close account refund recipient is sent lamports from the fast market order account -### Place initial offer (shimless) +### Place initial offer (shim) What is expected: - Fast market order is initialised - Initial offer is placed - Auction account is created and corresponds to a vaa and the initial offer -### Place initial offer (shim) +### Place initial offer (shimless) What is expected: - Fast market order is posted as a vaa diff --git a/solana/modules/matching-engine-testing/tests/utils/constants.rs b/solana/modules/matching-engine-testing/tests/utils/constants.rs index 1ad9d421f..407acf3f5 100644 --- a/solana/modules/matching-engine-testing/tests/utils/constants.rs +++ b/solana/modules/matching-engine-testing/tests/utils/constants.rs @@ -184,14 +184,14 @@ impl Chain { // Registered Token Routers lazy_static::lazy_static! { - pub static ref REGISTERED_TOKEN_ROUTERS: std::collections::HashMap> = { + pub static ref REGISTERED_TOKEN_ROUTERS: std::collections::HashMap = { let mut m = std::collections::HashMap::new(); - m.insert(Chain::Ethereum, vec![0xf0; 32]); - m.insert(Chain::Avalanche, vec![0xf1; 32]); - m.insert(Chain::Optimism, vec![0xf2; 32]); - m.insert(Chain::Arbitrum, vec![0xf3; 32]); - m.insert(Chain::Base, vec![0xf6; 32]); - m.insert(Chain::Polygon, vec![0xf7; 32]); + m.insert(Chain::Ethereum, [0xf0; 32]); + m.insert(Chain::Avalanche, [0xf1; 32]); + m.insert(Chain::Optimism, [0xf2; 32]); + m.insert(Chain::Arbitrum, [0xf3; 32]); + m.insert(Chain::Base, [0xf6; 32]); + m.insert(Chain::Polygon, [0xf7; 32]); m }; } From 75a0ad89c2b99a8addc532d45e4fb402b756dd74 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 23 Apr 2025 15:27:38 +0100 Subject: [PATCH 072/112] attended rest of comments --- solana/Cargo.lock | 12 ++ solana/Cargo.toml | 8 +- solana/clippy.toml | 4 +- .../matching-engine-testing/Cargo.toml | 11 - .../tests/testing_engine/setup.rs | 10 +- .../tests/utils/auction.rs | 7 +- .../tests/utils/public_keys.rs | 15 +- .../tests/utils/router.rs | 5 +- .../wormhole-matching-engine-api/Cargo.toml | 23 ++ .../src/data/fast_market_order.rs | 201 ++++++++++++++++++ .../src/data/initial_offer.rs | 13 ++ .../src/data/mod.rs | 8 + .../src/data/order_response.rs | 148 +++++++++++++ .../src/instructions.rs | 27 +++ .../wormhole-matching-engine-api/src/lib.rs | 6 + .../wormhole-matching-engine-api/src/utils.rs | 1 + .../src/fallback/processor/execute_order.rs | 18 +- .../processor/initialise_fast_market_order.rs | 6 +- .../fallback/processor/place_initial_offer.rs | 6 +- solana/ts/tests/01__matchingEngine.ts | 2 +- 20 files changed, 465 insertions(+), 66 deletions(-) create mode 100644 solana/modules/wormhole-matching-engine-api/Cargo.toml create mode 100644 solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/data/mod.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/data/order_response.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/instructions.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/lib.rs create mode 100644 solana/modules/wormhole-matching-engine-api/src/utils.rs diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 839efc20e..15c375d5e 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -6929,6 +6929,18 @@ version = "0.3.0-alpha.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9a873b44870f1f20c68683a83f0350a34893f94f04149cbce494f7049e6101e" +[[package]] +name = "wormhole-matching-engine-api" +version = "0.0.0" +dependencies = [ + "anchor-lang", + "anchor-spl", + "bytemuck", + "liquidity-layer-common-solana", + "solana-program", + "wormhole-svm-definitions", +] + [[package]] name = "wormhole-raw-vaas" version = "0.3.0-alpha.0" diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 45a2ed597..563282a94 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -61,10 +61,10 @@ overflow-checks = true # lto = "fat" # codegen-units = 1 -# [profile.release.build-override] -# opt-level = 3 -# incremental = false -# codegen-units = 1 +[profile.release.build-override] +opt-level = 3 +incremental = false +codegen-units = 1 [workspace.lints.clippy] correctness = { priority = -1, level = "warn"} diff --git a/solana/clippy.toml b/solana/clippy.toml index b36810917..b06464d4e 100644 --- a/solana/clippy.toml +++ b/solana/clippy.toml @@ -4,6 +4,4 @@ disallowed-methods = [ { path = "std::option::Option::map_or", reason = "prefer `map_or_else` for lazy evaluation" }, { path = "std::option::Option::ok_or", reason = "prefer `ok_or_else` for lazy evaluation" }, { path = "std::option::Option::unwrap_or", reason = "prefer `unwrap_or_else` for lazy evaluation" }, -] - -allow-expect-in-tests = true \ No newline at end of file +] \ No newline at end of file diff --git a/solana/modules/matching-engine-testing/Cargo.toml b/solana/modules/matching-engine-testing/Cargo.toml index 17a9eb9f8..175a90593 100644 --- a/solana/modules/matching-engine-testing/Cargo.toml +++ b/solana/modules/matching-engine-testing/Cargo.toml @@ -10,21 +10,10 @@ repository.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["no-idl"] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] mainnet = ["matching-engine/mainnet", "common/mainnet"] testnet = ["matching-engine/testnet", "common/testnet"] localnet = ["matching-engine/localnet", "common/localnet"] integration-test = ["localnet"] -idl-build = [ - "localnet", - "common/idl-build", - "anchor-lang/idl-build", - "anchor-spl/idl-build" -] [dev-dependencies] matching-engine.workspace = true diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 7f8ba95ad..90e889bbe 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -1071,14 +1071,8 @@ pub async fn setup_environment( Some(vaa_args_plural) => { let mut vaas_test_temp = TestVaaPairs::new(); for vaa_args in vaa_args_plural { - let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum] - .clone() - .try_into() - .expect("Failed to convert registered token router address to bytes [u8; 32]"); - let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum] - .clone() - .try_into() - .expect("Failed to convert registered token router address to bytes [u8; 32]"); + let arbitrum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Arbitrum]; + let ethereum_emitter_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&Chain::Ethereum]; let new_vaas_test = match transfer_direction { TransferDirection::FromArbitrumToEthereum => { create_vaas_test_with_chain_and_address( diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index abd5c4b60..0fd826465 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -437,12 +437,7 @@ impl AuctionAccounts { current_state: &TestingEngineState, testing_context: &TestingContext, ) -> Self { - let router_endpoints = current_state - .router_endpoints() - .clone() - .unwrap() - .endpoints - .clone(); + let router_endpoints = current_state.router_endpoints().unwrap().endpoints.clone(); let actor = testing_context.testing_actors.owner.clone(); let transfer_direction = testing_context.transfer_direction; let auction_config = Pubkey::find_program_address(&[AuctionConfig::SEED_PREFIX], &ID).0; diff --git a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs index b35098162..a0cb949ea 100644 --- a/solana/modules/matching-engine-testing/tests/utils/public_keys.rs +++ b/solana/modules/matching-engine-testing/tests/utils/public_keys.rs @@ -147,18 +147,9 @@ impl ChainAddress { pub fn from_registered_token_router(chain: Chain) -> Self { match chain { - Chain::Arbitrum => Self::new_with_address( - chain, - REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), - ), - Chain::Ethereum => Self::new_with_address( - chain, - REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), - ), - Chain::Solana => Self::new_with_address( - chain, - REGISTERED_TOKEN_ROUTERS[&chain].clone().try_into().unwrap(), - ), + Chain::Arbitrum => Self::new_with_address(chain, REGISTERED_TOKEN_ROUTERS[&chain]), + Chain::Ethereum => Self::new_with_address(chain, REGISTERED_TOKEN_ROUTERS[&chain]), + Chain::Solana => Self::new_with_address(chain, REGISTERED_TOKEN_ROUTERS[&chain]), _ => panic!("Unsupported chain"), } } diff --git a/solana/modules/matching-engine-testing/tests/utils/router.rs b/solana/modules/matching-engine-testing/tests/utils/router.rs index 3d5099672..8ad865f90 100644 --- a/solana/modules/matching-engine-testing/tests/utils/router.rs +++ b/solana/modules/matching-engine-testing/tests/utils/router.rs @@ -210,10 +210,7 @@ pub async fn add_cctp_router_endpoint_ix( system_program: anchor_lang::system_program::ID, }; - let registered_token_router_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&chain] - .clone() - .try_into() - .expect("Failed to convert registered token router address to bytes [u8; 32]"); + let registered_token_router_address: [u8; 32] = REGISTERED_TOKEN_ROUTERS[&chain]; let ix_data = AddCctpRouterEndpoint { args: AddCctpRouterEndpointArgs { chain: chain.as_chain_id(), diff --git a/solana/modules/wormhole-matching-engine-api/Cargo.toml b/solana/modules/wormhole-matching-engine-api/Cargo.toml new file mode 100644 index 000000000..a00c319ac --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "wormhole-matching-engine-api" +edition.workspace = true +version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wormhole-svm-definitions.workspace = true +bytemuck.workspace = true +anchor-spl.workspace = true +anchor-lang = { workspace = true, features = ["event-cpi", "init-if-needed"] } +solana-program.workspace = true +common.workspace = true + + +[lints] +workspace = true + diff --git a/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs b/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs new file mode 100644 index 000000000..6541895c1 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs @@ -0,0 +1,201 @@ +use bytemuck::{Pod, Zeroable}; + +use anchor_lang::prelude::*; +use anchor_lang::Discriminator; +use solana_program::keccak; +use wormhole_svm_definitions::make_anchor_discriminator; + +/// An account that represents a fast market order vaa. It is created by the signer of the transaction, and owned by the matching engine program. +/// The of the account is able to close this account and redeem the lamports deposited into the account (for rent) +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct FastMarketOrder { + /// The amount of tokens sent from the source chain via the fast transfer + pub amount_in: u64, + /// The minimum amount of tokens to be received on the target chain via the fast transfer + pub min_amount_out: u64, + /// The deadline of the auction + pub deadline: u32, + /// The target chain (represented as a wormhole chain id) + pub target_chain: u16, + /// The length of the redeemer message + pub redeemer_message_length: u16, + /// The redeemer of the fast transfer (on the destination chain) + pub redeemer: [u8; 32], + /// The sender of the fast transfer (on the source chain) + pub sender: [u8; 32], + /// The refund address of the fast transfer + pub refund_address: [u8; 32], + /// The maximum fee of the fast transfer + pub max_fee: u64, + /// The initial auction fee of the fast transfer + pub init_auction_fee: u64, + /// The redeemer message of the fast transfer + pub redeemer_message: [u8; 512], + /// The refund recipient for the creator of the fast market order account + pub close_account_refund_recipient: [u8; 32], + /// The emitter address of the fast transfer + pub vaa_emitter_address: [u8; 32], + /// The sequence of the fast transfer vaa + pub vaa_sequence: u64, + /// The timestamp of the fast transfer vaa + pub vaa_timestamp: u32, + /// The vaa nonce, which is not used and can be set to 0 + pub vaa_nonce: u32, + /// The source chain of the fast transfer vaa (represented as a wormhole chain id) + pub vaa_emitter_chain: u16, + /// The consistency level of the fast transfer vaa + pub vaa_consistency_level: u8, + /// Not used, but required for bytemuck serialisation + _padding: [u8; 5], +} + +pub struct FastMarketOrderParams { + pub amount_in: u64, + pub min_amount_out: u64, + pub deadline: u32, + pub target_chain: u16, + pub redeemer_message_length: u16, + pub redeemer: [u8; 32], + pub sender: [u8; 32], + pub refund_address: [u8; 32], + pub max_fee: u64, + pub init_auction_fee: u64, + pub redeemer_message: [u8; 512], + pub close_account_refund_recipient: [u8; 32], + pub vaa_sequence: u64, + pub vaa_timestamp: u32, + pub vaa_nonce: u32, + pub vaa_emitter_chain: u16, + pub vaa_consistency_level: u8, + pub vaa_emitter_address: [u8; 32], +} + +impl FastMarketOrder { + pub fn new(params: FastMarketOrderParams) -> Self { + Self { + amount_in: params.amount_in, + min_amount_out: params.min_amount_out, + deadline: params.deadline, + target_chain: params.target_chain, + redeemer_message_length: params.redeemer_message_length, + redeemer: params.redeemer, + sender: params.sender, + refund_address: params.refund_address, + max_fee: params.max_fee, + init_auction_fee: params.init_auction_fee, + redeemer_message: params.redeemer_message, + close_account_refund_recipient: params.close_account_refund_recipient, + vaa_sequence: params.vaa_sequence, + vaa_timestamp: params.vaa_timestamp, + vaa_nonce: params.vaa_nonce, + vaa_emitter_chain: params.vaa_emitter_chain, + vaa_consistency_level: params.vaa_consistency_level, + vaa_emitter_address: params.vaa_emitter_address, + _padding: [0_u8; 5], + } + } + + pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; + + /// Convert the fast market order to a vec of bytes (without the discriminator) + pub fn to_vec(&self) -> Vec { + let payload_slice = bytemuck::bytes_of(self); + let mut payload = Vec::with_capacity(payload_slice.len()); + payload.extend_from_slice(payload_slice); + payload + } + + /// Creates an payload as expected in a fast market order vaa + pub fn payload(&self) -> Vec { + let mut payload = vec![]; + payload.push(11_u8); // This is the payload id for a fast market order + payload.extend_from_slice(&self.amount_in.to_be_bytes()); + payload.extend_from_slice(&self.min_amount_out.to_be_bytes()); + payload.extend_from_slice(&self.target_chain.to_be_bytes()); + payload.extend_from_slice(&self.redeemer); + payload.extend_from_slice(&self.sender); + payload.extend_from_slice(&self.refund_address); + payload.extend_from_slice(&self.max_fee.to_be_bytes()); + payload.extend_from_slice(&self.init_auction_fee.to_be_bytes()); + payload.extend_from_slice(&self.deadline.to_be_bytes()); + payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); + if self.redeemer_message_length > 0 { + payload.extend_from_slice( + &self.redeemer_message[..usize::from(self.redeemer_message_length)], + ); + } + payload + } + + /// A double hash of the serialised fast market order. Used for seeds and verification. + pub fn digest(&self) -> [u8; 32] { + let message_hash = keccak::hashv(&[ + self.vaa_timestamp.to_be_bytes().as_ref(), + self.vaa_nonce.to_be_bytes().as_ref(), + self.vaa_emitter_chain.to_be_bytes().as_ref(), + &self.vaa_emitter_address, + &self.vaa_sequence.to_be_bytes(), + &[self.vaa_consistency_level], + self.payload().as_ref(), + ]); + // Digest is the hash of the message + keccak::hashv(&[message_hash.as_ref()]) + .as_ref() + .try_into() + .unwrap() + } + + /// Read from an account info + pub fn try_read(data: &[u8]) -> Result<&Self> { + if data.len() < 8 { + return Err(ErrorCode::AccountDiscriminatorNotFound.into()); + } + let discriminator: [u8; 8] = data[0..8].try_into().unwrap(); + if discriminator != Self::discriminator() { + return Err(ErrorCode::AccountDiscriminatorMismatch.into()); + } + let byte_muck_data = &data[8..]; + let fast_market_order = bytemuck::from_bytes::(byte_muck_data); + Ok(fast_market_order) + } +} + +impl Discriminator for FastMarketOrder { + const DISCRIMINATOR: [u8; 8] = make_anchor_discriminator(Self::SEED_PREFIX); +} + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct InitialiseFastMarketOrderData { + /// The fast market order as the bytemuck struct + pub fast_market_order: FastMarketOrder, + /// The guardian set bump + pub guardian_set_bump: u8, + /// Padding to ensure bytemuck deserialization works + _padding: [u8; 7], +} + +impl InitialiseFastMarketOrderData { + /// Creates a new InitialiseFastMarketOrderData with padding + pub fn new(fast_market_order: FastMarketOrder, guardian_set_bump: u8) -> Self { + Self { + fast_market_order, + guardian_set_bump, + _padding: [0_u8; 7], + } + } + + /// Deserializes the InitialiseFastMarketOrderData from a byte slice + /// + /// # Arguments + /// + /// * `data` - A byte slice containing the InitialiseFastMarketOrderData + /// + /// # Returns + /// + /// Option<&Self> - The deserialized InitialiseFastMarketOrderData or None if the byte slice is not the correct length + pub fn from_bytes(data: &[u8]) -> Option<&Self> { + bytemuck::try_from_bytes::(data).ok() + } +} diff --git a/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs b/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs new file mode 100644 index 000000000..9e4d4ccb8 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs @@ -0,0 +1,13 @@ +use bytemuck::{Pod, Zeroable}; + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct PlaceInitialOfferCctpShimData { + pub offer_price: u64, +} + +impl PlaceInitialOfferCctpShimData { + pub fn from_bytes(data: &[u8]) -> Option<&Self> { + bytemuck::try_from_bytes::(data).ok() + } +} diff --git a/solana/modules/wormhole-matching-engine-api/src/data/mod.rs b/solana/modules/wormhole-matching-engine-api/src/data/mod.rs new file mode 100644 index 000000000..440e1d617 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/data/mod.rs @@ -0,0 +1,8 @@ +mod fast_market_order; +mod initial_offer; +mod order_response; + +// Re-export all data structures +pub use fast_market_order::*; +pub use initial_offer::*; +pub use order_response::*; diff --git a/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs b/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs new file mode 100644 index 000000000..93ad7266c --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs @@ -0,0 +1,148 @@ +use anchor_lang::prelude::*; +use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; +use common::wormhole_cctp_solana::messages::Deposit; +use common::wormhole_io::TypePrefixedPayload; +use solana_program::keccak; + +use super::FastMarketOrder; + +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct PrepareOrderResponseCctpShimData { + pub encoded_cctp_message: Vec, + pub cctp_attestation: Vec, + pub finalized_vaa_message_args: FinalizedVaaMessageArgs, +} + +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct FinalizedVaaMessageArgs { + pub base_fee: u64, // Can also get from deposit payload + pub consistency_level: u8, + pub guardian_set_bump: u8, +} + +impl FinalizedVaaMessageArgs { + pub fn digest( + &self, + vaa_message_body_header: VaaMessageBodyHeader, + deposit_vaa_payload: Deposit, + ) -> [u8; 32] { + let message_hash = keccak::hashv(&[ + vaa_message_body_header.vaa_time.to_be_bytes().as_ref(), + vaa_message_body_header.nonce.to_be_bytes().as_ref(), + vaa_message_body_header.emitter_chain.to_be_bytes().as_ref(), + &vaa_message_body_header.emitter_address, + &vaa_message_body_header.sequence.to_be_bytes(), + &[vaa_message_body_header.consistency_level], + deposit_vaa_payload.to_vec().as_ref(), + ]); + // Digest is the hash of the message + keccak::hashv(&[message_hash.as_ref()]) + .as_ref() + .try_into() + .unwrap() + } +} + +impl PrepareOrderResponseCctpShimData { + pub fn from_bytes(data: &[u8]) -> Option { + Self::try_from_slice(data).ok() + } + + pub fn to_receive_message_args(&self) -> ReceiveMessageArgs { + let mut encoded_message = Vec::with_capacity(self.encoded_cctp_message.len()); + encoded_message.extend_from_slice(&self.encoded_cctp_message); + let mut cctp_attestation = Vec::with_capacity(self.cctp_attestation.len()); + cctp_attestation.extend_from_slice(&self.cctp_attestation); + ReceiveMessageArgs { + encoded_message, + attestation: cctp_attestation, + } + } +} + +/// VaaMessageBodyHeader for the digest calculation +/// +/// This is the header of the vaa message body. It is used to calculate the digest of the fast market order. +#[derive(Debug)] +pub struct VaaMessageBodyHeader { + pub consistency_level: u8, + pub vaa_time: u32, + pub nonce: u32, + pub sequence: u64, + pub emitter_chain: u16, + pub emitter_address: [u8; 32], +} + +impl VaaMessageBodyHeader { + pub fn new( + consistency_level: u8, + vaa_time: u32, + sequence: u64, + emitter_chain: u16, + emitter_address: [u8; 32], + ) -> Self { + Self { + consistency_level, + vaa_time, + nonce: 0, + sequence, + emitter_chain, + emitter_address, + } + } + + /// This function creates both the message body for the fast market order, including the payload. + pub fn message_body(&self, fast_market_order: &FastMarketOrder) -> Vec { + let mut message_body = vec![]; + message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); + message_body.extend_from_slice(&self.nonce.to_be_bytes()); + message_body.extend_from_slice(&self.emitter_chain.to_be_bytes()); + message_body.extend_from_slice(&self.emitter_address); + message_body.extend_from_slice(&self.sequence.to_be_bytes()); + message_body.extend_from_slice(&[self.consistency_level]); + message_body.push(11_u8); + message_body.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer); + message_body.extend_from_slice(&fast_market_order.sender); + message_body.extend_from_slice(&fast_market_order.refund_address); + message_body.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); + if fast_market_order.redeemer_message_length > 0 { + message_body.extend_from_slice( + &fast_market_order.redeemer_message + [..usize::from(fast_market_order.redeemer_message_length)], + ); + } + message_body + } + + /// This function creates the hash of the message body for the fast market order. + /// This is used to create the digest. + pub fn message_hash(&self, fast_market_order: &FastMarketOrder) -> keccak::Hash { + keccak::hashv(&[self.message_body(fast_market_order).as_ref()]) + } + + /// The digest is the hash of the message hash. + pub fn digest(&self, fast_market_order: &FastMarketOrder) -> keccak::Hash { + keccak::hashv(&[self.message_hash(fast_market_order).as_ref()]) + } + + /// This function returns the vaa time. + pub fn vaa_time(&self) -> u32 { + self.vaa_time + } + + /// This function returns the sequence number of the fast market order. + pub fn sequence(&self) -> u64 { + self.sequence + } + + /// This function returns the emitter chain of the fast market order. + pub fn emitter_chain(&self) -> u16 { + self.emitter_chain + } +} diff --git a/solana/modules/wormhole-matching-engine-api/src/instructions.rs b/solana/modules/wormhole-matching-engine-api/src/instructions.rs new file mode 100644 index 000000000..80eaf1da9 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/instructions.rs @@ -0,0 +1,27 @@ +use wormhole_svm_definitions::make_anchor_discriminator; + +use crate::data::{ + InitialiseFastMarketOrderData, PlaceInitialOfferCctpShimData, PrepareOrderResponseCctpShimData, +}; + +/// Enum representing all possible instructions for the Fallback Matching Engine +pub enum FallbackMatchingEngineInstruction<'ix> { + InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), + CloseFastMarketOrder, + PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), + ExecuteOrderCctpShim, + PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), +} + +impl<'ix> FallbackMatchingEngineInstruction<'ix> { + pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:initialise_fast_market_order"); + pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:close_fast_market_order"); + pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); + pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:execute_order_cctp_shim"); + pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); +} diff --git a/solana/modules/wormhole-matching-engine-api/src/lib.rs b/solana/modules/wormhole-matching-engine-api/src/lib.rs new file mode 100644 index 000000000..fb69a0651 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/lib.rs @@ -0,0 +1,6 @@ +mod data; +mod instructions; +mod utils; + +pub use data::*; +pub use instructions::*; diff --git a/solana/modules/wormhole-matching-engine-api/src/utils.rs b/solana/modules/wormhole-matching-engine-api/src/utils.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/solana/modules/wormhole-matching-engine-api/src/utils.rs @@ -0,0 +1 @@ + diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index d86ba5a22..943a41277 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -159,8 +159,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not require_min_account_infos_len(accounts, 31)?; - let program_id = &ID; - // Get the accounts let signer_account = &accounts[0]; let cctp_message_account = &accounts[1]; @@ -216,7 +214,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { ]; let (cctp_message_pda, cctp_message_bump) = - Pubkey::find_program_address(&cctp_message_seeds, program_id); + Pubkey::find_program_address(&cctp_message_seeds, &ID); if cctp_message_pda != cctp_message_account.key() { msg!("Cctp message seeds are invalid"); return Err(ErrorCode::ConstraintSeeds.into()) @@ -224,10 +222,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; // Check custodian owner - if custodian_account.owner != program_id { + if custodian_account.owner != &ID { msg!( "Custodian owner is invalid: expected {}, got {}", - program_id, + &ID, custodian_account.owner ); return Err(ErrorCode::ConstraintOwner.into()) @@ -248,7 +246,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { ]; let (fast_market_order_pda, _fast_market_order_bump) = - Pubkey::find_program_address(&fast_market_order_seeds, program_id); + Pubkey::find_program_address(&fast_market_order_seeds, &ID); if fast_market_order_pda != fast_market_order_account.key() { msg!("Fast market order seeds are invalid"); return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { @@ -257,14 +255,14 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; // Check fast market order is owned by the matching engine program - if fast_market_order_account.owner != program_id { + if fast_market_order_account.owner != &ID { msg!("Fast market order is not owned by the matching engine program"); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("fast_market_order")); }; // Check active auction owner - if active_auction_account.owner != program_id { + if active_auction_account.owner != &ID { msg!("Active auction is not owned by the matching engine program"); return Err(ErrorCode::ConstraintOwner.into()) .map_err(|e: Error| e.with_account_name("active_auction")); @@ -286,7 +284,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { active_auction.vaa_hash.as_ref(), &[active_auction.bump], ], - program_id, + &ID, ) .map_err(|_| { msg!("Failed to create program address with known bump"); @@ -313,7 +311,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { active_auction_account.key().as_ref(), &[active_auction.info.as_ref().unwrap().custody_token_bump], ], - program_id, + &ID, ) .map_err(|_| { msg!("Failed to create program address with known bump"); diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 65160f7bc..79434fc7c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -114,8 +114,6 @@ pub fn initialise_fast_market_order( ) -> Result<()> { require_min_account_infos_len(accounts, 6)?; - let program_id = ID; - let signer = &accounts[0]; let fast_market_order_account = &accounts[1]; let guardian_set = &accounts[2]; @@ -157,7 +155,7 @@ pub fn initialise_fast_market_order( fast_market_order_vaa_digest.as_ref(), fast_market_order.close_account_refund_recipient.as_ref(), ], - &program_id, + &ID, ); if fast_market_order_pda != fast_market_order_key { @@ -179,7 +177,7 @@ pub fn initialise_fast_market_order( fast_market_order_account.lamports(), space, accounts, - &program_id, + &ID, fast_market_order_signer_seeds, )?; // Borrow the account data mutably diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 528acba1b..3fb9c168a 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -71,7 +71,7 @@ impl<'ix> PlaceInitialOfferCctpShimAccounts<'ix> { AccountMeta::new_readonly(*self.auction_config, false), AccountMeta::new_readonly(*self.from_endpoint, false), AccountMeta::new_readonly(*self.to_endpoint, false), - AccountMeta::new(*self.fast_market_order, false), + AccountMeta::new_readonly(*self.fast_market_order, false), AccountMeta::new(*self.auction, false), AccountMeta::new(*self.offer_token, false), AccountMeta::new(*self.auction_custody_token, false), @@ -358,7 +358,7 @@ pub fn place_initial_offer_cctp_shim( from_endpoint_account.chain, from_endpoint_account.address, ); - let vaa_message_digest = vaa_message.digest(&fast_market_order_zero_copy); + let vaa_message_digest = vaa_message.digest(fast_market_order_zero_copy); // Begin of initialisation of auction custody token account // ------------------------------------------------------------------------------------------------ @@ -441,7 +441,7 @@ pub fn place_initial_offer_cctp_shim( let auction_to_write = Auction { bump, vaa_hash: vaa_message - .digest(&fast_market_order_zero_copy) + .digest(fast_market_order_zero_copy) .as_ref() .try_into() .unwrap(), diff --git a/solana/ts/tests/01__matchingEngine.ts b/solana/ts/tests/01__matchingEngine.ts index d4bba7a5b..ab66879eb 100644 --- a/solana/ts/tests/01__matchingEngine.ts +++ b/solana/ts/tests/01__matchingEngine.ts @@ -3972,11 +3972,11 @@ describe("Matching Engine", function () { if (!ix.programId.equals(engine.ID) || !("data" in ix)) { continue; } + const data = utils.bytes.bs58.decode(ix.data); if (!data.subarray(0, 8).equals(CPI_EVENT_IX_SELECTOR)) { continue; } - console.log("data", data); const decoded = engine.program.coder.events.decode( utils.bytes.base64.encode(data.subarray(8)), ); From 2e727bc46629c904748d86656bdde1685c09917e Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 23 Apr 2025 15:29:38 +0100 Subject: [PATCH 073/112] removed newly added crate --- solana/Cargo.lock | 12 -- .../wormhole-matching-engine-api/Cargo.toml | 23 -- .../src/data/fast_market_order.rs | 201 ------------------ .../src/data/initial_offer.rs | 13 -- .../src/data/mod.rs | 8 - .../src/data/order_response.rs | 148 ------------- .../src/instructions.rs | 27 --- .../wormhole-matching-engine-api/src/lib.rs | 6 - .../wormhole-matching-engine-api/src/utils.rs | 1 - 9 files changed, 439 deletions(-) delete mode 100644 solana/modules/wormhole-matching-engine-api/Cargo.toml delete mode 100644 solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/data/mod.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/data/order_response.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/instructions.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/lib.rs delete mode 100644 solana/modules/wormhole-matching-engine-api/src/utils.rs diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 15c375d5e..839efc20e 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -6929,18 +6929,6 @@ version = "0.3.0-alpha.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9a873b44870f1f20c68683a83f0350a34893f94f04149cbce494f7049e6101e" -[[package]] -name = "wormhole-matching-engine-api" -version = "0.0.0" -dependencies = [ - "anchor-lang", - "anchor-spl", - "bytemuck", - "liquidity-layer-common-solana", - "solana-program", - "wormhole-svm-definitions", -] - [[package]] name = "wormhole-raw-vaas" version = "0.3.0-alpha.0" diff --git a/solana/modules/wormhole-matching-engine-api/Cargo.toml b/solana/modules/wormhole-matching-engine-api/Cargo.toml deleted file mode 100644 index a00c319ac..000000000 --- a/solana/modules/wormhole-matching-engine-api/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "wormhole-matching-engine-api" -edition.workspace = true -version.workspace = true -authors.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -wormhole-svm-definitions.workspace = true -bytemuck.workspace = true -anchor-spl.workspace = true -anchor-lang = { workspace = true, features = ["event-cpi", "init-if-needed"] } -solana-program.workspace = true -common.workspace = true - - -[lints] -workspace = true - diff --git a/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs b/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs deleted file mode 100644 index 6541895c1..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/data/fast_market_order.rs +++ /dev/null @@ -1,201 +0,0 @@ -use bytemuck::{Pod, Zeroable}; - -use anchor_lang::prelude::*; -use anchor_lang::Discriminator; -use solana_program::keccak; -use wormhole_svm_definitions::make_anchor_discriminator; - -/// An account that represents a fast market order vaa. It is created by the signer of the transaction, and owned by the matching engine program. -/// The of the account is able to close this account and redeem the lamports deposited into the account (for rent) -#[derive(Debug, Copy, Clone, Pod, Zeroable)] -#[repr(C)] -pub struct FastMarketOrder { - /// The amount of tokens sent from the source chain via the fast transfer - pub amount_in: u64, - /// The minimum amount of tokens to be received on the target chain via the fast transfer - pub min_amount_out: u64, - /// The deadline of the auction - pub deadline: u32, - /// The target chain (represented as a wormhole chain id) - pub target_chain: u16, - /// The length of the redeemer message - pub redeemer_message_length: u16, - /// The redeemer of the fast transfer (on the destination chain) - pub redeemer: [u8; 32], - /// The sender of the fast transfer (on the source chain) - pub sender: [u8; 32], - /// The refund address of the fast transfer - pub refund_address: [u8; 32], - /// The maximum fee of the fast transfer - pub max_fee: u64, - /// The initial auction fee of the fast transfer - pub init_auction_fee: u64, - /// The redeemer message of the fast transfer - pub redeemer_message: [u8; 512], - /// The refund recipient for the creator of the fast market order account - pub close_account_refund_recipient: [u8; 32], - /// The emitter address of the fast transfer - pub vaa_emitter_address: [u8; 32], - /// The sequence of the fast transfer vaa - pub vaa_sequence: u64, - /// The timestamp of the fast transfer vaa - pub vaa_timestamp: u32, - /// The vaa nonce, which is not used and can be set to 0 - pub vaa_nonce: u32, - /// The source chain of the fast transfer vaa (represented as a wormhole chain id) - pub vaa_emitter_chain: u16, - /// The consistency level of the fast transfer vaa - pub vaa_consistency_level: u8, - /// Not used, but required for bytemuck serialisation - _padding: [u8; 5], -} - -pub struct FastMarketOrderParams { - pub amount_in: u64, - pub min_amount_out: u64, - pub deadline: u32, - pub target_chain: u16, - pub redeemer_message_length: u16, - pub redeemer: [u8; 32], - pub sender: [u8; 32], - pub refund_address: [u8; 32], - pub max_fee: u64, - pub init_auction_fee: u64, - pub redeemer_message: [u8; 512], - pub close_account_refund_recipient: [u8; 32], - pub vaa_sequence: u64, - pub vaa_timestamp: u32, - pub vaa_nonce: u32, - pub vaa_emitter_chain: u16, - pub vaa_consistency_level: u8, - pub vaa_emitter_address: [u8; 32], -} - -impl FastMarketOrder { - pub fn new(params: FastMarketOrderParams) -> Self { - Self { - amount_in: params.amount_in, - min_amount_out: params.min_amount_out, - deadline: params.deadline, - target_chain: params.target_chain, - redeemer_message_length: params.redeemer_message_length, - redeemer: params.redeemer, - sender: params.sender, - refund_address: params.refund_address, - max_fee: params.max_fee, - init_auction_fee: params.init_auction_fee, - redeemer_message: params.redeemer_message, - close_account_refund_recipient: params.close_account_refund_recipient, - vaa_sequence: params.vaa_sequence, - vaa_timestamp: params.vaa_timestamp, - vaa_nonce: params.vaa_nonce, - vaa_emitter_chain: params.vaa_emitter_chain, - vaa_consistency_level: params.vaa_consistency_level, - vaa_emitter_address: params.vaa_emitter_address, - _padding: [0_u8; 5], - } - } - - pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; - - /// Convert the fast market order to a vec of bytes (without the discriminator) - pub fn to_vec(&self) -> Vec { - let payload_slice = bytemuck::bytes_of(self); - let mut payload = Vec::with_capacity(payload_slice.len()); - payload.extend_from_slice(payload_slice); - payload - } - - /// Creates an payload as expected in a fast market order vaa - pub fn payload(&self) -> Vec { - let mut payload = vec![]; - payload.push(11_u8); // This is the payload id for a fast market order - payload.extend_from_slice(&self.amount_in.to_be_bytes()); - payload.extend_from_slice(&self.min_amount_out.to_be_bytes()); - payload.extend_from_slice(&self.target_chain.to_be_bytes()); - payload.extend_from_slice(&self.redeemer); - payload.extend_from_slice(&self.sender); - payload.extend_from_slice(&self.refund_address); - payload.extend_from_slice(&self.max_fee.to_be_bytes()); - payload.extend_from_slice(&self.init_auction_fee.to_be_bytes()); - payload.extend_from_slice(&self.deadline.to_be_bytes()); - payload.extend_from_slice(&self.redeemer_message_length.to_be_bytes()); - if self.redeemer_message_length > 0 { - payload.extend_from_slice( - &self.redeemer_message[..usize::from(self.redeemer_message_length)], - ); - } - payload - } - - /// A double hash of the serialised fast market order. Used for seeds and verification. - pub fn digest(&self) -> [u8; 32] { - let message_hash = keccak::hashv(&[ - self.vaa_timestamp.to_be_bytes().as_ref(), - self.vaa_nonce.to_be_bytes().as_ref(), - self.vaa_emitter_chain.to_be_bytes().as_ref(), - &self.vaa_emitter_address, - &self.vaa_sequence.to_be_bytes(), - &[self.vaa_consistency_level], - self.payload().as_ref(), - ]); - // Digest is the hash of the message - keccak::hashv(&[message_hash.as_ref()]) - .as_ref() - .try_into() - .unwrap() - } - - /// Read from an account info - pub fn try_read(data: &[u8]) -> Result<&Self> { - if data.len() < 8 { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - let discriminator: [u8; 8] = data[0..8].try_into().unwrap(); - if discriminator != Self::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - let byte_muck_data = &data[8..]; - let fast_market_order = bytemuck::from_bytes::(byte_muck_data); - Ok(fast_market_order) - } -} - -impl Discriminator for FastMarketOrder { - const DISCRIMINATOR: [u8; 8] = make_anchor_discriminator(Self::SEED_PREFIX); -} - -#[derive(Debug, Copy, Clone, Pod, Zeroable)] -#[repr(C)] -pub struct InitialiseFastMarketOrderData { - /// The fast market order as the bytemuck struct - pub fast_market_order: FastMarketOrder, - /// The guardian set bump - pub guardian_set_bump: u8, - /// Padding to ensure bytemuck deserialization works - _padding: [u8; 7], -} - -impl InitialiseFastMarketOrderData { - /// Creates a new InitialiseFastMarketOrderData with padding - pub fn new(fast_market_order: FastMarketOrder, guardian_set_bump: u8) -> Self { - Self { - fast_market_order, - guardian_set_bump, - _padding: [0_u8; 7], - } - } - - /// Deserializes the InitialiseFastMarketOrderData from a byte slice - /// - /// # Arguments - /// - /// * `data` - A byte slice containing the InitialiseFastMarketOrderData - /// - /// # Returns - /// - /// Option<&Self> - The deserialized InitialiseFastMarketOrderData or None if the byte slice is not the correct length - pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() - } -} diff --git a/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs b/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs deleted file mode 100644 index 9e4d4ccb8..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/data/initial_offer.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bytemuck::{Pod, Zeroable}; - -#[derive(Debug, Copy, Clone, Pod, Zeroable)] -#[repr(C)] -pub struct PlaceInitialOfferCctpShimData { - pub offer_price: u64, -} - -impl PlaceInitialOfferCctpShimData { - pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() - } -} diff --git a/solana/modules/wormhole-matching-engine-api/src/data/mod.rs b/solana/modules/wormhole-matching-engine-api/src/data/mod.rs deleted file mode 100644 index 440e1d617..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/data/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod fast_market_order; -mod initial_offer; -mod order_response; - -// Re-export all data structures -pub use fast_market_order::*; -pub use initial_offer::*; -pub use order_response::*; diff --git a/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs b/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs deleted file mode 100644 index 93ad7266c..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/data/order_response.rs +++ /dev/null @@ -1,148 +0,0 @@ -use anchor_lang::prelude::*; -use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; -use common::wormhole_cctp_solana::messages::Deposit; -use common::wormhole_io::TypePrefixedPayload; -use solana_program::keccak; - -use super::FastMarketOrder; - -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] -pub struct PrepareOrderResponseCctpShimData { - pub encoded_cctp_message: Vec, - pub cctp_attestation: Vec, - pub finalized_vaa_message_args: FinalizedVaaMessageArgs, -} - -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] -pub struct FinalizedVaaMessageArgs { - pub base_fee: u64, // Can also get from deposit payload - pub consistency_level: u8, - pub guardian_set_bump: u8, -} - -impl FinalizedVaaMessageArgs { - pub fn digest( - &self, - vaa_message_body_header: VaaMessageBodyHeader, - deposit_vaa_payload: Deposit, - ) -> [u8; 32] { - let message_hash = keccak::hashv(&[ - vaa_message_body_header.vaa_time.to_be_bytes().as_ref(), - vaa_message_body_header.nonce.to_be_bytes().as_ref(), - vaa_message_body_header.emitter_chain.to_be_bytes().as_ref(), - &vaa_message_body_header.emitter_address, - &vaa_message_body_header.sequence.to_be_bytes(), - &[vaa_message_body_header.consistency_level], - deposit_vaa_payload.to_vec().as_ref(), - ]); - // Digest is the hash of the message - keccak::hashv(&[message_hash.as_ref()]) - .as_ref() - .try_into() - .unwrap() - } -} - -impl PrepareOrderResponseCctpShimData { - pub fn from_bytes(data: &[u8]) -> Option { - Self::try_from_slice(data).ok() - } - - pub fn to_receive_message_args(&self) -> ReceiveMessageArgs { - let mut encoded_message = Vec::with_capacity(self.encoded_cctp_message.len()); - encoded_message.extend_from_slice(&self.encoded_cctp_message); - let mut cctp_attestation = Vec::with_capacity(self.cctp_attestation.len()); - cctp_attestation.extend_from_slice(&self.cctp_attestation); - ReceiveMessageArgs { - encoded_message, - attestation: cctp_attestation, - } - } -} - -/// VaaMessageBodyHeader for the digest calculation -/// -/// This is the header of the vaa message body. It is used to calculate the digest of the fast market order. -#[derive(Debug)] -pub struct VaaMessageBodyHeader { - pub consistency_level: u8, - pub vaa_time: u32, - pub nonce: u32, - pub sequence: u64, - pub emitter_chain: u16, - pub emitter_address: [u8; 32], -} - -impl VaaMessageBodyHeader { - pub fn new( - consistency_level: u8, - vaa_time: u32, - sequence: u64, - emitter_chain: u16, - emitter_address: [u8; 32], - ) -> Self { - Self { - consistency_level, - vaa_time, - nonce: 0, - sequence, - emitter_chain, - emitter_address, - } - } - - /// This function creates both the message body for the fast market order, including the payload. - pub fn message_body(&self, fast_market_order: &FastMarketOrder) -> Vec { - let mut message_body = vec![]; - message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); - message_body.extend_from_slice(&self.nonce.to_be_bytes()); - message_body.extend_from_slice(&self.emitter_chain.to_be_bytes()); - message_body.extend_from_slice(&self.emitter_address); - message_body.extend_from_slice(&self.sequence.to_be_bytes()); - message_body.extend_from_slice(&[self.consistency_level]); - message_body.push(11_u8); - message_body.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.redeemer); - message_body.extend_from_slice(&fast_market_order.sender); - message_body.extend_from_slice(&fast_market_order.refund_address); - message_body.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); - if fast_market_order.redeemer_message_length > 0 { - message_body.extend_from_slice( - &fast_market_order.redeemer_message - [..usize::from(fast_market_order.redeemer_message_length)], - ); - } - message_body - } - - /// This function creates the hash of the message body for the fast market order. - /// This is used to create the digest. - pub fn message_hash(&self, fast_market_order: &FastMarketOrder) -> keccak::Hash { - keccak::hashv(&[self.message_body(fast_market_order).as_ref()]) - } - - /// The digest is the hash of the message hash. - pub fn digest(&self, fast_market_order: &FastMarketOrder) -> keccak::Hash { - keccak::hashv(&[self.message_hash(fast_market_order).as_ref()]) - } - - /// This function returns the vaa time. - pub fn vaa_time(&self) -> u32 { - self.vaa_time - } - - /// This function returns the sequence number of the fast market order. - pub fn sequence(&self) -> u64 { - self.sequence - } - - /// This function returns the emitter chain of the fast market order. - pub fn emitter_chain(&self) -> u16 { - self.emitter_chain - } -} diff --git a/solana/modules/wormhole-matching-engine-api/src/instructions.rs b/solana/modules/wormhole-matching-engine-api/src/instructions.rs deleted file mode 100644 index 80eaf1da9..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/instructions.rs +++ /dev/null @@ -1,27 +0,0 @@ -use wormhole_svm_definitions::make_anchor_discriminator; - -use crate::data::{ - InitialiseFastMarketOrderData, PlaceInitialOfferCctpShimData, PrepareOrderResponseCctpShimData, -}; - -/// Enum representing all possible instructions for the Fallback Matching Engine -pub enum FallbackMatchingEngineInstruction<'ix> { - InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), - CloseFastMarketOrder, - PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), - ExecuteOrderCctpShim, - PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), -} - -impl<'ix> FallbackMatchingEngineInstruction<'ix> { - pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:initialise_fast_market_order"); - pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:close_fast_market_order"); - pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); - pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:execute_order_cctp_shim"); - pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); -} diff --git a/solana/modules/wormhole-matching-engine-api/src/lib.rs b/solana/modules/wormhole-matching-engine-api/src/lib.rs deleted file mode 100644 index fb69a0651..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod data; -mod instructions; -mod utils; - -pub use data::*; -pub use instructions::*; diff --git a/solana/modules/wormhole-matching-engine-api/src/utils.rs b/solana/modules/wormhole-matching-engine-api/src/utils.rs deleted file mode 100644 index 8b1378917..000000000 --- a/solana/modules/wormhole-matching-engine-api/src/utils.rs +++ /dev/null @@ -1 +0,0 @@ - From bcb8b05caff3df42f7c01e5b7e1d474dca2e7241 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 23 Apr 2025 15:35:40 +0100 Subject: [PATCH 074/112] make idl --- solana/ts/src/idl/json/matching_engine.json | 8 ++++---- solana/ts/src/idl/ts/matching_engine.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index d7eb17f4a..e86dc99b3 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -3152,12 +3152,12 @@ "name": "BorshDeserializationError" }, { - "code": 7798, - "name": "InvalidPda" + "code": 7797, + "name": "BorshSerializationError" }, { - "code": 7800, - "name": "AccountDataTooSmall" + "code": 7798, + "name": "InvalidPda" }, { "code": 7802, diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 5fcd92ae5..7f2d76875 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -3158,12 +3158,12 @@ export type MatchingEngine = { "name": "borshDeserializationError" }, { - "code": 7798, - "name": "invalidPda" + "code": 7797, + "name": "borshSerializationError" }, { - "code": 7800, - "name": "accountDataTooSmall" + "code": 7798, + "name": "invalidPda" }, { "code": 7802, From f02aa8369154d8d5912251ba43a385f236063ea5 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 15:43:52 +0100 Subject: [PATCH 075/112] close market refund recipient is pubkey instead of bytes --- .../tests/shimful/fast_market_order_shim.rs | 4 ++-- .../tests/testing_engine/engine.rs | 8 +++----- .../src/fallback/processor/close_fast_market_order.rs | 8 ++++---- .../matching-engine/src/fallback/processor/helpers.rs | 1 + .../src/fallback/processor/place_initial_offer.rs | 2 +- .../matching-engine/src/state/fast_market_order.rs | 4 ++-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index c9a86fb9e..875c9d277 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -94,7 +94,7 @@ fn initialise_fast_market_order_fallback_instruction( &[ FastMarketOrderState::SEED_PREFIX, &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient, + &fast_market_order.close_account_refund_recipient.as_ref(), ], program_id, ) @@ -217,7 +217,7 @@ pub fn create_fast_market_order_state_from_vaa_data( max_fee: order.max_fee, init_auction_fee: order.init_auction_fee, redeemer_message: redeemer_message_fixed_length, - close_account_refund_recipient: close_account_refund_recipient.to_bytes(), + close_account_refund_recipient, vaa_sequence: vaa_data.sequence, vaa_timestamp: vaa_data.vaa_time, vaa_nonce: vaa_data.nonce, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 2f1ad38fb..88d19c8de 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -497,7 +497,7 @@ impl TestingEngine { &[ FastMarketOrder::SEED_PREFIX, &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient, + &fast_market_order.close_account_refund_recipient.as_ref(), ], &self.testing_context.get_matching_engine_program_id(), ); @@ -521,10 +521,8 @@ impl TestingEngine { fast_market_order_address: fast_market_order_account, fast_market_order_bump, fast_market_order, - close_account_refund_recipient: Pubkey::try_from_slice( - &fast_market_order.close_account_refund_recipient, - ) - .unwrap(), + close_account_refund_recipient: fast_market_order + .close_account_refund_recipient, }, guardian_set_state: GuardianSetState { guardian_set_address: guardian_signature_info.guardian_set_pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 02b2aab02..2d81c91e4 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -63,12 +63,12 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { let fast_market_order_deserialized = FastMarketOrder::try_read(fast_market_order_data)?; // Check that the fast_market_order is owned by the close_account_refund_recipient if fast_market_order_deserialized.close_account_refund_recipient - != close_account_refund_recipient.key().as_ref() + != close_account_refund_recipient.key() { return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( |e: Error| { e.with_pubkeys(( - Pubkey::from(fast_market_order_deserialized.close_account_refund_recipient), + fast_market_order_deserialized.close_account_refund_recipient, close_account_refund_recipient.key(), )) }, @@ -76,12 +76,12 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { } if fast_market_order_deserialized.close_account_refund_recipient - != close_account_refund_recipient.key().as_ref() + != close_account_refund_recipient.key() { return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( |e: Error| { e.with_pubkeys(( - Pubkey::from(fast_market_order_deserialized.close_account_refund_recipient), + fast_market_order_deserialized.close_account_refund_recipient, close_account_refund_recipient.key(), )) }, diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 5b2ada6b0..00ccb9e27 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -144,6 +144,7 @@ pub fn create_account_reliably( /// * `data_len` - The length of the data to be written to the token account. /// * `accounts` - The accounts to be used in the CPI. /// * `signer_seeds` - The signer seeds to be used in the CPI. +//TODO: Fix clippy warning #[allow(clippy::too_many_arguments)] pub fn create_token_account_reliably( payer_pubkey: &Pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 3fb9c168a..05f3acd62 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -523,7 +523,7 @@ mod tests { max_fee: 0, init_auction_fee: 0, redeemer_message: [0_u8; 512], - close_account_refund_recipient: [0_u8; 32], + close_account_refund_recipient: Pubkey::default(), vaa_sequence: 0, vaa_timestamp: 0, vaa_nonce: 0, diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 845491276..41223876c 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -31,7 +31,7 @@ pub struct FastMarketOrder { /// The redeemer message of the fast transfer pub redeemer_message: [u8; 512], /// The refund recipient for the creator of the fast market order account - pub close_account_refund_recipient: [u8; 32], + pub close_account_refund_recipient: Pubkey, /// The emitter address of the fast transfer pub vaa_emitter_address: [u8; 32], /// The sequence of the fast transfer vaa @@ -60,7 +60,7 @@ pub struct FastMarketOrderParams { pub max_fee: u64, pub init_auction_fee: u64, pub redeemer_message: [u8; 512], - pub close_account_refund_recipient: [u8; 32], + pub close_account_refund_recipient: Pubkey, pub vaa_sequence: u64, pub vaa_timestamp: u32, pub vaa_nonce: u32, From 3ad6d500e7d299971310d376d59630e3eb79e7c1 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:36:00 +0100 Subject: [PATCH 076/112] american english spelling adopted --- .../matching-engine-testing/tests/README.md | 8 ++--- .../tests/shimful/fast_market_order_shim.rs | 32 ++++++++--------- .../create_and_close_fast_market_order.rs | 10 +++--- .../tests/test_scenarios/execute_order.rs | 16 ++++----- .../test_scenarios/initialise_and_misc.rs | 8 ++--- .../tests/test_scenarios/mod.rs | 2 +- .../tests/testing_engine/engine.rs | 4 +-- .../tests/testing_engine/setup.rs | 32 ++++++++--------- .../tests/utils/account_fixtures.rs | 2 +- .../tests/utils/program_fixtures.rs | 16 ++++----- .../tests/utils/token_account.rs | 2 +- .../processor/close_fast_market_order.rs | 2 +- .../processor/initialise_fast_market_order.rs | 34 +++++++++---------- .../src/fallback/processor/mod.rs | 2 +- .../fallback/processor/place_initial_offer.rs | 4 +-- .../processor/prepare_order_response.rs | 28 +++++++-------- .../fallback/processor/process_instruction.rs | 22 ++++++------ 17 files changed, 112 insertions(+), 112 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/README.md b/solana/modules/matching-engine-testing/tests/README.md index 3ea1a8920..b59c22aa9 100644 --- a/solana/modules/matching-engine-testing/tests/README.md +++ b/solana/modules/matching-engine-testing/tests/README.md @@ -10,7 +10,7 @@ Each test is a function that is annotated with `#[tokio::test]`. Each test is a test for a specific scenario, and uses the `TestingEngine` to execute a series of instruction triggers. -The `TestingEngine` is initialised with a `TestingContext`. The `TestingContext` holds the solana program test context, the actors, the transfer direction, created vaas, as well as some constants. +The `TestingEngine` is initialized with a `TestingContext`. The `TestingContext` holds the solana program test context, the actors, the transfer direction, created vaas, as well as some constants. The `TestingEngine` is used to execute the instruction triggers in the order they are provided. See the `testing_engine/engine.rs` file for more details. @@ -19,7 +19,7 @@ The `TestingEngine` is used to execute the instruction triggers in the order the ### Initialize program What is expected: -- Program is initialised +- Program is initialized - Router endpoints are created @@ -33,7 +33,7 @@ What is expected: What is expected: - Fast market order account is created - Guardian signatures account is created via Verify VAA Shim program, which are the signatures found in the fast market order VAA from the source network. -- Fast market order is initialised +- Fast market order is initialized ### Close fast market order @@ -44,7 +44,7 @@ What is expected: ### Place initial offer (shim) What is expected: -- Fast market order is initialised +- Fast market order is initialized - Initial offer is placed - Auction account is created and corresponds to a vaa and the initial offer diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 875c9d277..32a3c48d5 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -8,10 +8,10 @@ use matching_engine::fallback::close_fast_market_order::{ CloseFastMarketOrder as CloseFastMarketOrderFallback, CloseFastMarketOrderAccounts as CloseFastMarketOrderFallbackAccounts, }; -use matching_engine::fallback::initialise_fast_market_order::{ - InitialiseFastMarketOrder as InitialiseFastMarketOrderFallback, - InitialiseFastMarketOrderAccounts as InitialiseFastMarketOrderFallbackAccounts, - InitialiseFastMarketOrderData as InitialiseFastMarketOrderFallbackData, +use matching_engine::fallback::initialize_fast_market_order::{ + InitializeFastMarketOrder as InitializeFastMarketOrderFallback, + InitializeFastMarketOrderAccounts as InitializeFastMarketOrderFallbackAccounts, + InitializeFastMarketOrderData as InitializeFastMarketOrderFallbackData, }; use utils::constants::*; @@ -21,9 +21,9 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction use std::rc::Rc; use wormhole_io::TypePrefixedPayload; -/// Initialise the fast market order account +/// Initialize the fast market order account /// -/// This function initialises the fast market order account +/// This function initializes the fast market order account /// /// # Arguments /// @@ -38,7 +38,7 @@ use wormhole_io::TypePrefixedPayload; /// # Asserts /// /// * The expected error, if any, is reached when executing the instruction -pub async fn initialise_fast_market_order_fallback( +pub async fn initialize_fast_market_order_fallback( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Rc, @@ -47,7 +47,7 @@ pub async fn initialise_fast_market_order_fallback( expected_error: Option<&ExpectedError>, ) { let program_id = &testing_context.get_matching_engine_program_id(); - let initialise_fast_market_order_ix = initialise_fast_market_order_fallback_instruction( + let initialize_fast_market_order_ix = initialize_fast_market_order_fallback_instruction( payer_signer, program_id, fast_market_order, @@ -56,7 +56,7 @@ pub async fn initialise_fast_market_order_fallback( let transaction = testing_context .create_transaction( test_context, - &[initialise_fast_market_order_ix], + &[initialize_fast_market_order_ix], Some(&payer_signer.pubkey()), &[payer_signer], 1000000000, @@ -68,9 +68,9 @@ pub async fn initialise_fast_market_order_fallback( .await; } -/// Creates the initialise fast market order fallback instruction +/// Creates the initialize fast market order fallback instruction /// -/// This function creates the initialise fast market order fallback instruction +/// This function creates the initialize fast market order fallback instruction /// /// # Arguments /// @@ -83,8 +83,8 @@ pub async fn initialise_fast_market_order_fallback( /// /// # Returns /// -/// * `Instruction` - The initialise fast market order fallback instruction -fn initialise_fast_market_order_fallback_instruction( +/// * `Instruction` - The initialize fast market order fallback instruction +fn initialize_fast_market_order_fallback_instruction( payer_signer: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, @@ -100,7 +100,7 @@ fn initialise_fast_market_order_fallback_instruction( ) .0; - let create_fast_market_order_accounts = InitialiseFastMarketOrderFallbackAccounts { + let create_fast_market_order_accounts = InitializeFastMarketOrderFallbackAccounts { signer: &payer_signer.pubkey(), fast_market_order_account: &fast_market_order_account, guardian_set: &guardian_signature_info.guardian_set_pubkey, @@ -109,10 +109,10 @@ fn initialise_fast_market_order_fallback_instruction( system_program: &solana_program::system_program::ID, }; - InitialiseFastMarketOrderFallback { + InitializeFastMarketOrderFallback { program_id, accounts: create_fast_market_order_accounts, - data: InitialiseFastMarketOrderFallbackData::new( + data: InitializeFastMarketOrderFallbackData::new( fast_market_order, guardian_signature_info.guardian_set_bump, ), diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs index 75427a5a0..0adf5f10b 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/create_and_close_fast_market_order.rs @@ -6,13 +6,13 @@ //! //! ### Happy path tests //! -//! - `test_initialise_fast_market_order_fallback` - Test that the fast market order is initialised correctly +//! - `test_initialize_fast_market_order_fallback` - Test that the fast market order is initialized correctly //! - `test_close_fast_market_order_fallback` - Test that the fast market order is closed correctly //! - `test_close_fast_market_order_fallback_with_custom_refund_recipient` - Test that the fast market order is closed correctly with a custom refund recipient //! //! ### Sad path tests //! -//! - `test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialise_it` - Test that the fast market order cannot be refunded by someone who did not initialise it +//! - `test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialize_it` - Test that the fast market order cannot be refunded by someone who did not initialize it //! //! ### Edge case tests //! @@ -55,7 +55,7 @@ use utils::vaa::VaaArgs; /// Test that the create fast market order account works correctly for the fallback instruction #[tokio::test] -pub async fn test_initialise_fast_market_order_fallback() { +pub async fn test_initialize_fast_market_order_fallback() { let vaa_args = vec![VaaArgs { post_vaa: false, ..VaaArgs::default() @@ -175,9 +175,9 @@ pub async fn test_close_fast_market_order_fallback_with_custom_refund_recipient( ***************** */ -/// Test that the fast market order cannot be refunded by someone who did not initialise it +/// Test that the fast market order cannot be refunded by someone who did not initialize it #[tokio::test] -pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialise_it() { +pub async fn test_fast_market_order_cannot_be_refunded_by_someone_who_did_not_initialize_it() { let transfer_direction = TransferDirection::FromArbitrumToEthereum; let vaa_args = vec![VaaArgs { post_vaa: false, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 3ece5c228..5855f82f2 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -1044,7 +1044,7 @@ pub async fn test_execute_order_shim_emitter_chain_mismatch() { ) .await; let testing_engine = TestingEngine::new(testing_context).await; - let initialise_first_fast_market_order_instruction_triggers = vec![ + let initialize_first_fast_market_order_instruction_triggers = vec![ InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), InstructionTrigger::CreateCctpRouterEndpoints( CreateCctpRouterEndpointsInstructionConfig::default(), @@ -1053,14 +1053,14 @@ pub async fn test_execute_order_shim_emitter_chain_mismatch() { InitializeFastMarketOrderShimInstructionConfig::default(), ), ]; - let initialise_first_fast_market_order_state = testing_engine + let initialize_first_fast_market_order_state = testing_engine .execute( &mut test_context, - initialise_first_fast_market_order_instruction_triggers, + initialize_first_fast_market_order_instruction_triggers, None, ) .await; - let initialise_second_fast_market_order_instruction_triggers = vec![ + let initialize_second_fast_market_order_instruction_triggers = vec![ InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), InstructionTrigger::InitializeFastMarketOrderShim( InitializeFastMarketOrderShimInstructionConfig { @@ -1070,11 +1070,11 @@ pub async fn test_execute_order_shim_emitter_chain_mismatch() { }, ), ]; - let initialise_second_fast_market_order_state = testing_engine + let initialize_second_fast_market_order_state = testing_engine .execute( &mut test_context, - initialise_second_fast_market_order_instruction_triggers, - Some(initialise_first_fast_market_order_state), + initialize_second_fast_market_order_instruction_triggers, + Some(initialize_first_fast_market_order_state), ) .await; let instruction_triggers = vec![InstructionTrigger::ExecuteOrderShim( @@ -1092,7 +1092,7 @@ pub async fn test_execute_order_shim_emitter_chain_mismatch() { .execute( &mut test_context, instruction_triggers, - Some(initialise_second_fast_market_order_state), + Some(initialize_second_fast_market_order_state), ) .await; } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs index b0b0d8bdf..8d9ddb993 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs @@ -1,12 +1,12 @@ -//! # Initialise and misc instruction testing +//! # Initialize and misc instruction testing //! -//! This module contains tests for the initialise and some other miscellaneous setup test scenarios. +//! This module contains tests for the initialize and some other miscellaneous setup test scenarios. //! //! ## Test Cases //! //! ### Happy path tests //! -//! - `test_initialize_program` - Test that the program is initialised correctly +//! - `test_initialize_program` - Test that the program is initialized correctly //! - `test_cctp_token_router_endpoint_creation` - Test that a CCTP token router endpoint is created for the arbitrum and ethereum chains //! - `test_local_token_router_endpoint_creation` - Test that a local token router endpoint is created for the arbitrum and ethereum chains //! - `test_setup_vaas` - Test that the vaas are setup correctly @@ -61,7 +61,7 @@ use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; ***************** */ -/// Test that the program is initialised correctly +/// Test that the program is initialized correctly #[tokio::test] pub async fn test_initialize_program() { let (testing_context, mut test_context) = setup_environment( diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs index b4b1f1ea4..412f26123 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/mod.rs @@ -1,6 +1,6 @@ pub mod create_and_close_fast_market_order; pub mod execute_order; -pub mod initialise_and_misc; +pub mod initialize_and_misc; pub mod make_offer; pub mod prepare_order; pub mod settle_auction; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 88d19c8de..a2d77a863 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -33,7 +33,7 @@ use super::setup::TestingContext; use super::{config::*, state::*}; use crate::shimful; use crate::shimful::fast_market_order_shim::{ - create_fast_market_order_state_from_vaa_data, initialise_fast_market_order_fallback, + create_fast_market_order_state_from_vaa_data, initialize_fast_market_order_fallback, }; use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; @@ -502,7 +502,7 @@ impl TestingEngine { &self.testing_context.get_matching_engine_program_id(), ); - initialise_fast_market_order_fallback( + initialize_fast_market_order_fallback( &self.testing_context, test_context, &payer_signer, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 90e889bbe..e912768e1 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -18,9 +18,9 @@ use crate::utils::airdrop::{airdrop, airdrop_spl_token}; use crate::utils::cctp_message::CctpRemoteTokenMessenger; use crate::utils::mint::MintFixture; use crate::utils::program_fixtures::{ - initialise_cctp_message_transmitter, initialise_cctp_token_messenger_minter, - initialise_local_token_router, initialise_post_message_shims, initialise_upgrade_manager, - initialise_verify_shims, initialise_wormhole_core_bridge, + initialize_cctp_message_transmitter, initialize_cctp_token_messenger_minter, + initialize_local_token_router, initialize_post_message_shims, initialize_upgrade_manager, + initialize_verify_shims, initialize_wormhole_core_bridge, }; use crate::utils::token_account::{ create_token_account, read_keypair_from_file, SplTokenEnum, TokenAccountFixture, @@ -113,26 +113,26 @@ impl PreTestingContext { // Setup Testing Actors let testing_actors = TestingActors::new(owner_keypair_path); println!("Testing actors: {:?}", testing_actors); - // Initialise Upgrade Manager - let program_data_pubkey = initialise_upgrade_manager( + // Initialize Upgrade Manager + let program_data_pubkey = initialize_upgrade_manager( &mut program_test, &program_id, testing_actors.owner.pubkey(), ); - // Initialise CCTP Token Messenger Minter - initialise_cctp_token_messenger_minter(&mut program_test); + // Initialize CCTP Token Messenger Minter + initialize_cctp_token_messenger_minter(&mut program_test); - // Initialise Wormhole Core Bridge - initialise_wormhole_core_bridge(&mut program_test); + // Initialize Wormhole Core Bridge + initialize_wormhole_core_bridge(&mut program_test); - // Initialise CCTP Message Transmitter - initialise_cctp_message_transmitter(&mut program_test); + // Initialize CCTP Message Transmitter + initialize_cctp_message_transmitter(&mut program_test); - // Initialise Local Token Router - initialise_local_token_router(&mut program_test); + // Initialize Local Token Router + initialize_local_token_router(&mut program_test); - // Initialise Account Fixtures + // Initialize Account Fixtures let account_fixtures = FixtureAccounts::new(&mut program_test); // Add lookup table accounts @@ -148,12 +148,12 @@ impl PreTestingContext { /// Adds the post message shims to the program test pub fn add_post_message_shims(&mut self) { - initialise_post_message_shims(&mut self.program_test); + initialize_post_message_shims(&mut self.program_test); } /// Adds the verify shims to the program test pub fn add_verify_shims(&mut self) { - initialise_verify_shims(&mut self.program_test); + initialize_verify_shims(&mut self.program_test); } } diff --git a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs index c3da0ad32..6cdf31ad6 100644 --- a/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/account_fixtures.rs @@ -34,7 +34,7 @@ pub struct FixtureAccounts { } impl FixtureAccounts { - /// Initialises all accounts in fixtures directory + /// Initializes all accounts in fixtures directory /// /// # Arguments /// diff --git a/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs b/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs index 927f5d1af..aecdc74f2 100644 --- a/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs +++ b/solana/modules/matching-engine-testing/tests/utils/program_fixtures.rs @@ -22,10 +22,10 @@ fn get_program_data(owner: Pubkey) -> Vec { bincode::serialize(&state).unwrap() } -/// Initialise the upgrade manager program +/// Initialize the upgrade manager program /// /// Returns the program data pubkey -pub fn initialise_upgrade_manager( +pub fn initialize_upgrade_manager( program_test: &mut ProgramTest, program_id: &Pubkey, owner_pubkey: Pubkey, @@ -52,34 +52,34 @@ pub fn initialise_upgrade_manager( program_data_pubkey } -pub fn initialise_cctp_token_messenger_minter(program_test: &mut ProgramTest) { +pub fn initialize_cctp_token_messenger_minter(program_test: &mut ProgramTest) { let program_id = CCTP_TOKEN_MESSENGER_MINTER_PID; program_test.add_program("mainnet_cctp_token_messenger_minter", program_id, None); } -pub fn initialise_wormhole_core_bridge(program_test: &mut ProgramTest) { +pub fn initialize_wormhole_core_bridge(program_test: &mut ProgramTest) { let program_id = CORE_BRIDGE_PID; program_test.add_program("mainnet_core_bridge", program_id, None); } -pub fn initialise_cctp_message_transmitter(program_test: &mut ProgramTest) { +pub fn initialize_cctp_message_transmitter(program_test: &mut ProgramTest) { let program_id = CCTP_MESSAGE_TRANSMITTER_PID; program_test.add_program("mainnet_cctp_message_transmitter", program_id, None); } -pub fn initialise_local_token_router(program_test: &mut ProgramTest) { +pub fn initialize_local_token_router(program_test: &mut ProgramTest) { let program_id = TOKEN_ROUTER_PID; program_test.add_program("token_router", program_id, None); } -pub fn initialise_post_message_shims(program_test: &mut ProgramTest) { +pub fn initialize_post_message_shims(program_test: &mut ProgramTest) { let post_message_program_id = WORMHOLE_POST_MESSAGE_SHIM_PID; program_test.add_program("wormhole_post_message_shim", post_message_program_id, None); let verify_vaa_shim_program_id = WORMHOLE_VERIFY_VAA_SHIM_PID; program_test.add_program("wormhole_verify_vaa_shim", verify_vaa_shim_program_id, None); } -pub fn initialise_verify_shims(program_test: &mut ProgramTest) { +pub fn initialize_verify_shims(program_test: &mut ProgramTest) { let verify_vaa_shim_program_id = WORMHOLE_VERIFY_VAA_SHIM_PID; program_test.add_program("wormhole_verify_vaa_shim", verify_vaa_shim_program_id, None); program_test.add_account_with_base64_data( diff --git a/solana/modules/matching-engine-testing/tests/utils/token_account.rs b/solana/modules/matching-engine-testing/tests/utils/token_account.rs index 94605788d..6407e323c 100644 --- a/solana/modules/matching-engine-testing/tests/utils/token_account.rs +++ b/solana/modules/matching-engine-testing/tests/utils/token_account.rs @@ -13,7 +13,7 @@ use solana_sdk::{ use std::fs; #[derive(Clone)] -/// A struct representing an initialised token account +/// A struct representing an initialized token account pub struct TokenAccountFixture { pub address: Pubkey, pub account: spl_token::state::Account, diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 2d81c91e4..38b44222b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -8,7 +8,7 @@ use super::helpers::require_min_account_infos_len; use super::FallbackMatchingEngineInstruction; pub struct CloseFastMarketOrderAccounts<'ix> { - /// The fast market order account created from the initialise fast market order instruction + /// The fast market order account created from the initialize fast market order instruction pub fast_market_order: &'ix Pubkey, /// The account that will receive the refund. CHECK: Must be a signer. /// CHECK: Must match the close account refund recipient in the fast market order account diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs index 79434fc7c..00d04e724 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs @@ -17,7 +17,7 @@ use crate::error::MatchingEngineError; use crate::state::FastMarketOrder as FastMarketOrderState; use crate::ID; -pub struct InitialiseFastMarketOrderAccounts<'ix> { +pub struct InitializeFastMarketOrderAccounts<'ix> { /// The signer of the transaction pub signer: &'ix Pubkey, /// The fast market order account pubkey (that is created by the instruction) @@ -32,7 +32,7 @@ pub struct InitialiseFastMarketOrderAccounts<'ix> { pub system_program: &'ix Pubkey, } -impl<'ix> InitialiseFastMarketOrderAccounts<'ix> { +impl<'ix> InitializeFastMarketOrderAccounts<'ix> { pub fn to_account_metas(&self) -> Vec { vec![ AccountMeta::new(*self.signer, true), // This will be the refund recipient @@ -47,7 +47,7 @@ impl<'ix> InitialiseFastMarketOrderAccounts<'ix> { #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] -pub struct InitialiseFastMarketOrderData { +pub struct InitializeFastMarketOrderData { /// The fast market order as the bytemuck struct pub fast_market_order: FastMarketOrderState, /// The guardian set bump @@ -55,8 +55,8 @@ pub struct InitialiseFastMarketOrderData { /// Padding to ensure bytemuck deserialization works _padding: [u8; 7], } -impl InitialiseFastMarketOrderData { - // Adds the padding to the InitialiseFastMarketOrderData +impl InitializeFastMarketOrderData { + // Adds the padding to the InitializeFastMarketOrderData pub fn new(fast_market_order: FastMarketOrderState, guardian_set_bump: u8) -> Self { Self { fast_market_order, @@ -65,37 +65,37 @@ impl InitialiseFastMarketOrderData { } } - /// Deserializes the InitialiseFastMarketOrderData from a byte slice + /// Deserializes the InitializeFastMarketOrderData from a byte slice /// /// # Arguments /// - /// * `data` - A byte slice containing the InitialiseFastMarketOrderData + /// * `data` - A byte slice containing the InitializeFastMarketOrderData /// /// # Returns /// - /// Option<&Self> - The deserialized InitialiseFastMarketOrderData or None if the byte slice is not the correct length + /// Option<&Self> - The deserialized InitializeFastMarketOrderData or None if the byte slice is not the correct length pub fn from_bytes(data: &[u8]) -> Option<&Self> { bytemuck::try_from_bytes::(data).ok() } } -pub struct InitialiseFastMarketOrder<'ix> { +pub struct InitializeFastMarketOrder<'ix> { pub program_id: &'ix Pubkey, - pub accounts: InitialiseFastMarketOrderAccounts<'ix>, - pub data: InitialiseFastMarketOrderData, + pub accounts: InitializeFastMarketOrderAccounts<'ix>, + pub data: InitializeFastMarketOrderData, } -impl InitialiseFastMarketOrder<'_> { +impl InitializeFastMarketOrder<'_> { pub fn instruction(&self) -> Instruction { Instruction { program_id: *self.program_id, accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(&self.data).to_vec(), + data: FallbackMatchingEngineInstruction::InitializeFastMarketOrder(&self.data).to_vec(), } } } -/// Initialises the fast market order account +/// Initializes the fast market order account /// /// The verify shim program first checks that the digest of the fast market order is correct, and that the guardian signature is correct and recoverable. /// If this is the case, the fast market order account is created. The fast market order account is owned by the matching engine program. It can be closed @@ -108,9 +108,9 @@ impl InitialiseFastMarketOrder<'_> { /// # Returns /// /// Result<()> -pub fn initialise_fast_market_order( +pub fn initialize_fast_market_order( accounts: &[AccountInfo], - data: &InitialiseFastMarketOrderData, + data: &InitializeFastMarketOrderData, ) -> Result<()> { require_min_account_infos_len(accounts, 6)?; @@ -119,7 +119,7 @@ pub fn initialise_fast_market_order( let guardian_set = &accounts[2]; let guardian_set_signatures = &accounts[3]; - let InitialiseFastMarketOrderData { + let InitializeFastMarketOrderData { fast_market_order, guardian_set_bump, _padding: _, diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 259faa1e8..6fcba8152 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -3,7 +3,7 @@ pub use process_instruction::*; pub mod burn_and_post; pub mod close_fast_market_order; pub mod execute_order; -pub mod initialise_fast_market_order; +pub mod initialize_fast_market_order; pub mod place_initial_offer; pub mod prepare_order_response; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 05f3acd62..ccee6b469 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -46,9 +46,9 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub from_endpoint: &'ix Pubkey, /// The to endpoint account pub to_endpoint: &'ix Pubkey, - /// The fast market order account, which will be initialised. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] + /// The fast market order account, which will be initialized. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] pub fast_market_order: &'ix Pubkey, - /// The auction account, which will be initialised + /// The auction account, which will be initialized pub auction: &'ix Pubkey, /// The offer token account pub offer_token: &'ix Pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 7eba0bc64..cdb8e94ab 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -228,14 +228,14 @@ pub fn prepare_order_response_cctp_shim( ErrorCode::ConstraintOwner ); - // Check that custodian deserialises correctly + // Check that custodian deserializes correctly let _checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; - // Deserialise the to_endpoint account + // Deserialize the to_endpoint account let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..]).map(Box::new)?; - // Deserialise the from_endpoint account + // Deserialize the from_endpoint account let from_endpoint_account = RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..]).map(Box::new)?; @@ -343,13 +343,13 @@ pub fn prepare_order_response_cctp_shim( MatchingEngineError::InvalidProgram ); - // Construct the finalised vaa message digest data + // Construct the finalized vaa message digest data let finalized_vaa_message_digest = { - let finalised_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; - let finalised_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); - let finalised_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; - let finalised_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; - let finalised_vaa_consistency_level = finalized_vaa_message_args.consistency_level; + let finalized_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; + let finalized_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); + let finalized_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; + let finalized_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; + let finalized_vaa_consistency_level = finalized_vaa_message_args.consistency_level; let slow_order_response = SlowOrderResponse { base_fee: finalized_vaa_message_args.base_fee, }; @@ -366,11 +366,11 @@ pub fn prepare_order_response_cctp_shim( finalized_vaa_message_args.digest( VaaMessageBodyHeader::new( - finalised_vaa_consistency_level, - finalised_vaa_timestamp, - finalised_vaa_sequence, - finalised_vaa_emitter_chain, - finalised_vaa_emitter_address, + finalized_vaa_consistency_level, + finalized_vaa_timestamp, + finalized_vaa_sequence, + finalized_vaa_emitter_chain, + finalized_vaa_emitter_address, ), deposit_vaa_payload, ) diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index ad2544c1c..7e88c765b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -1,7 +1,7 @@ use super::close_fast_market_order::close_fast_market_order; use super::execute_order::handle_execute_order_shim; -use super::initialise_fast_market_order::{ - initialise_fast_market_order, InitialiseFastMarketOrderData, +use super::initialize_fast_market_order::{ + initialize_fast_market_order, InitializeFastMarketOrderData, }; use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; use super::prepare_order_response::prepare_order_response_cctp_shim; @@ -11,8 +11,8 @@ use anchor_lang::prelude::*; use wormhole_svm_definitions::make_anchor_discriminator; impl<'ix> FallbackMatchingEngineInstruction<'ix> { - pub const INITIALISE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = - make_anchor_discriminator(b"global:initialise_fast_market_order"); + pub const INITIALIZE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + make_anchor_discriminator(b"global:initialize_fast_market_order"); pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = make_anchor_discriminator(b"global:close_fast_market_order"); pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = @@ -24,7 +24,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { } pub enum FallbackMatchingEngineInstruction<'ix> { - InitialiseFastMarketOrder(&'ix InitialiseFastMarketOrderData), + InitializeFastMarketOrder(&'ix InitializeFastMarketOrderData), CloseFastMarketOrder, PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, @@ -42,8 +42,8 @@ pub fn process_instruction( let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data).unwrap(); match instruction { - FallbackMatchingEngineInstruction::InitialiseFastMarketOrder(data) => { - initialise_fast_market_order(accounts, data) + FallbackMatchingEngineInstruction::InitializeFastMarketOrder(data) => { + initialize_fast_market_order(accounts, data) } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { close_fast_market_order(accounts) @@ -73,8 +73,8 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { )) } - FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR => Some( - Self::InitialiseFastMarketOrder(bytemuck::from_bytes(&instruction_data[8..])), + FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR => Some( + Self::InitializeFastMarketOrder(bytemuck::from_bytes(&instruction_data[8..])), ), FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) @@ -124,14 +124,14 @@ impl FallbackMatchingEngineInstruction<'_> { out } - Self::InitialiseFastMarketOrder(data) => { + Self::InitializeFastMarketOrder(data) => { let data_slice = bytemuck::bytes_of(*data); let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length let mut out = Vec::with_capacity(total_capacity); out.extend_from_slice( - &FallbackMatchingEngineInstruction::INITIALISE_FAST_MARKET_ORDER_SELECTOR, + &FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR, ); out.extend_from_slice(data_slice); From 66c04f82dbcf513b9973b22b43cac6c7f5084b54 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:39:48 +0100 Subject: [PATCH 077/112] do not have to check from endpoint cctp --- .../src/fallback/processor/prepare_order_response.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index cdb8e94ab..451dbf8c1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -289,15 +289,6 @@ pub fn prepare_order_response_cctp_shim( MatchingEngineError::InvalidEndpoint ); - // Check that the from endpoint protocol is cctp or local - require!( - matches!( - from_endpoint_account.protocol, - MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } - ), - MatchingEngineError::InvalidEndpoint - ); - // Check that to endpoint chain is equal to the fast_market_order target_chain require_eq!( to_endpoint_account.chain, From e39d9fe6fe7823a829b2abb0c92c491cec2d41dd Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:45:25 +0100 Subject: [PATCH 078/112] make lint and rename of files --- .../{initialise_and_misc.rs => initialize_and_misc.rs} | 0 .../matching-engine/src/fallback/processor/helpers.rs | 6 ++---- ...fast_market_order.rs => initialize_fast_market_order.rs} | 0 .../src/fallback/processor/place_initial_offer.rs | 2 -- .../src/fallback/processor/prepare_order_response.rs | 2 -- 5 files changed, 2 insertions(+), 8 deletions(-) rename solana/modules/matching-engine-testing/tests/test_scenarios/{initialise_and_misc.rs => initialize_and_misc.rs} (100%) rename solana/programs/matching-engine/src/fallback/processor/{initialise_fast_market_order.rs => initialize_fast_market_order.rs} (100%) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs similarity index 100% rename from solana/modules/matching-engine-testing/tests/test_scenarios/initialise_and_misc.rs rename to solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 00ccb9e27..0d0c994d9 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; +use solana_program::program_pack::Pack; use solana_program::{ entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, @@ -144,14 +145,11 @@ pub fn create_account_reliably( /// * `data_len` - The length of the data to be written to the token account. /// * `accounts` - The accounts to be used in the CPI. /// * `signer_seeds` - The signer seeds to be used in the CPI. -//TODO: Fix clippy warning -#[allow(clippy::too_many_arguments)] pub fn create_token_account_reliably( payer_pubkey: &Pubkey, account_pubkey_to_create: &Pubkey, owner_account_pubkey: &Pubkey, mint_pubkey: &Pubkey, - data_len: usize, token_account_lamports: u64, accounts: &[AccountInfo], signer_seeds: &[&[&[u8]]], @@ -161,7 +159,7 @@ pub fn create_token_account_reliably( payer_pubkey, account_pubkey_to_create, token_account_lamports, - data_len, + spl_token::state::Account::LEN, accounts, &spl_token::ID, signer_seeds, diff --git a/solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs similarity index 100% rename from solana/programs/matching-engine/src/fallback/processor/initialise_fast_market_order.rs rename to solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index ccee6b469..5edafc318 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -15,7 +15,6 @@ use common::TRANSFER_AUTHORITY_SEED_PREFIX; use solana_program::instruction::Instruction; use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; -use solana_program::program_pack::Pack; use super::FallbackMatchingEngineInstruction; use crate::error::MatchingEngineError; @@ -390,7 +389,6 @@ pub fn place_initial_offer_cctp_shim( &auction_custody_token_pda, &auction_account.key(), &usdc.key(), - spl_token::state::Account::LEN, auction_custody_token.lamports(), accounts, auction_custody_token_signer_seeds, diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 451dbf8c1..cb647a4cd 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -25,7 +25,6 @@ use ruint::aliases::U256; use solana_program::instruction::Instruction; use solana_program::keccak; use solana_program::program::invoke_signed_unchecked; -use solana_program::program_pack::Pack; use wormhole_io::WriteableBytes; use crate::error::MatchingEngineError; @@ -467,7 +466,6 @@ pub fn prepare_order_response_cctp_shim( &prepared_custody_token_pda, &prepared_order_response_pda, &usdc.key(), - spl_token::state::Account::LEN, prepared_custody_token.lamports(), accounts, prepared_custody_token_signer_seeds, From 599293372ba54bb30c49a7dbe56cb769de656b3d Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:46:24 +0100 Subject: [PATCH 079/112] removed unnecessary program log --- .../src/fallback/processor/prepare_order_response.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index cb647a4cd..5afb9de8e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -506,10 +506,6 @@ pub fn prepare_order_response_cctp_shim( receive_message_args, )?; - msg!( - "Attempting to transfer {} from cctp mint recipient to prepared custody token", - fast_market_order_zero_copy.amount_in - ); // Finally transfer minted via CCTP to prepared custody token. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, From 65f26bdfd6ca86545984086b1b66129f4a356ed9 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:48:21 +0100 Subject: [PATCH 080/112] create token account only creates usdc token account --- .../matching-engine/src/fallback/processor/helpers.rs | 6 +++--- .../src/fallback/processor/place_initial_offer.rs | 5 ++--- .../src/fallback/processor/prepare_order_response.rs | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 0d0c994d9..73f645067 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,5 +1,6 @@ use anchor_lang::prelude::*; +use anchor_spl::mint::USDC; use anchor_spl::token::spl_token; use solana_program::program_pack::Pack; use solana_program::{ @@ -145,11 +146,10 @@ pub fn create_account_reliably( /// * `data_len` - The length of the data to be written to the token account. /// * `accounts` - The accounts to be used in the CPI. /// * `signer_seeds` - The signer seeds to be used in the CPI. -pub fn create_token_account_reliably( +pub fn create_usdc_token_account_reliably( payer_pubkey: &Pubkey, account_pubkey_to_create: &Pubkey, owner_account_pubkey: &Pubkey, - mint_pubkey: &Pubkey, token_account_lamports: u64, accounts: &[AccountInfo], signer_seeds: &[&[&[u8]]], @@ -169,7 +169,7 @@ pub fn create_token_account_reliably( let init_token_account_ix = spl_token::instruction::initialize_account3( &spl_token::ID, account_pubkey_to_create, - mint_pubkey, + &USDC, owner_account_pubkey, )?; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 5edafc318..46b5d0e8a 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,5 +1,5 @@ use super::helpers::create_account_reliably; -use super::helpers::create_token_account_reliably; +use super::helpers::create_usdc_token_account_reliably; use super::helpers::require_min_account_infos_len; use crate::state::MessageProtocol; use crate::state::{ @@ -384,11 +384,10 @@ pub fn place_initial_offer_cctp_shim( ]; let auction_custody_token_signer_seeds = &[&auction_custody_token_seeds[..]]; - create_token_account_reliably( + create_usdc_token_account_reliably( &signer.key(), &auction_custody_token_pda, &auction_account.key(), - &usdc.key(), auction_custody_token.lamports(), accounts, auction_custody_token_signer_seeds, diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 5afb9de8e..16bbb9c87 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use super::helpers::create_account_reliably; use super::place_initial_offer::VaaMessageBodyHeader; use super::FallbackMatchingEngineInstruction; -use crate::fallback::helpers::create_token_account_reliably; +use crate::fallback::helpers::create_usdc_token_account_reliably; use crate::fallback::helpers::require_min_account_infos_len; use crate::state::PreparedOrderResponseInfo; use crate::state::PreparedOrderResponseSeeds; @@ -461,11 +461,10 @@ pub fn prepare_order_response_cctp_shim( ]; let prepared_custody_token_signer_seeds = &[&create_prepared_custody_token_seeds[..]]; - create_token_account_reliably( + create_usdc_token_account_reliably( &signer.key(), &prepared_custody_token_pda, &prepared_order_response_pda, - &usdc.key(), prepared_custody_token.lamports(), accounts, prepared_custody_token_signer_seeds, From f2f7a07706693d1dba18d29f0ac6f09c32777920 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 16:53:28 +0100 Subject: [PATCH 081/112] fast market order as ref instead of owned for efficiency --- .../src/fallback/processor/initialize_fast_market_order.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index 00d04e724..260231906 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -123,14 +123,14 @@ pub fn initialize_fast_market_order( fast_market_order, guardian_set_bump, _padding: _, - } = *data; + } = data; // Start of cpi call to verify the shim. // ------------------------------------------------------------------------------------------------ let fast_market_order_vaa_digest = fast_market_order.digest(); let fast_market_order_vaa_digest_hash = keccak::Hash::try_from_slice(&fast_market_order_vaa_digest).unwrap(); let verify_hash_data = - VerifyHashData::new(guardian_set_bump, fast_market_order_vaa_digest_hash); + VerifyHashData::new(*guardian_set_bump, fast_market_order_vaa_digest_hash); let verify_hash_shim_ix = VerifyHash { program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, accounts: VerifyHashAccounts { @@ -187,7 +187,7 @@ pub fn initialize_fast_market_order( let discriminator = FastMarketOrderState::discriminator(); fast_market_order_account_data[0..8].copy_from_slice(&discriminator); - let fast_market_order_bytes = bytemuck::bytes_of(&data.fast_market_order); + let fast_market_order_bytes = bytemuck::bytes_of(fast_market_order); // Write the fast_market_order struct to the account fast_market_order_account_data[8..8_usize.saturating_add(fast_market_order_bytes.len())] From dd56e0f05d74e2d5442a13b38cd4d9399a989018 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 6 May 2025 17:09:55 +0100 Subject: [PATCH 082/112] helper function for custodian check --- .../src/fallback/processor/execute_order.rs | 12 ++---------- .../src/fallback/processor/helpers.rs | 7 +++++++ .../src/fallback/processor/place_initial_offer.rs | 15 +++------------ .../fallback/processor/prepare_order_response.rs | 6 +++--- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 943a41277..f81c97272 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,5 +1,5 @@ -use super::helpers::require_min_account_infos_len; use crate::fallback::burn_and_post::PostMessageDerivedAccounts; +use crate::fallback::helpers::*; use crate::state::{ Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, RouterEndpoint, @@ -222,15 +222,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; // Check custodian owner - if custodian_account.owner != &ID { - msg!( - "Custodian owner is invalid: expected {}, got {}", - &ID, - custodian_account.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - }; + check_custodian_owner_is_program_id(custodian_account)?; // Check custodian deserialises into a checked custodian account let _checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 73f645067..b9242c485 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,5 +1,6 @@ use anchor_lang::prelude::*; +use crate::ID; use anchor_spl::mint::USDC; use anchor_spl::token::spl_token; use solana_program::program_pack::Pack; @@ -18,6 +19,12 @@ pub fn require_min_account_infos_len(accounts: &[AccountInfo], len: usize) -> Re Ok(()) } +#[inline(always)] +pub fn check_custodian_owner_is_program_id(custodian: &AccountInfo) -> Result<()> { + require_eq!(custodian.owner, &ID, ErrorCode::ConstraintOwner); + Ok(()) +} + pub fn create_account_reliably( payer_key: &Pubkey, account_key: &Pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 46b5d0e8a..45a37bdae 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,6 +1,4 @@ -use super::helpers::create_account_reliably; -use super::helpers::create_usdc_token_account_reliably; -use super::helpers::require_min_account_infos_len; +use super::helpers::*; use crate::state::MessageProtocol; use crate::state::{ Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, @@ -239,15 +237,8 @@ pub fn place_initial_offer_cctp_shim( } // Check custodian owner - if custodian.owner != program_id { - msg!( - "Custodian owner is invalid: expected {}, got {}", - program_id, - custodian.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - } + check_custodian_owner_is_program_id(custodian)?; + // Check custodian is not paused let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; if checked_custodian.paused { diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 16bbb9c87..3954ebc31 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -3,6 +3,7 @@ use std::io::Cursor; use super::helpers::create_account_reliably; use super::place_initial_offer::VaaMessageBodyHeader; use super::FallbackMatchingEngineInstruction; +use crate::fallback::helpers::check_custodian_owner_is_program_id; use crate::fallback::helpers::create_usdc_token_account_reliably; use crate::fallback::helpers::require_min_account_infos_len; use crate::state::PreparedOrderResponseInfo; @@ -227,6 +228,8 @@ pub fn prepare_order_response_cctp_shim( ErrorCode::ConstraintOwner ); + // Check custodian owner + check_custodian_owner_is_program_id(custodian)?; // Check that custodian deserializes correctly let _checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; @@ -256,9 +259,6 @@ pub fn prepare_order_response_cctp_shim( let (prepared_custody_token_pda, prepared_custody_token_bump) = Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); - // Check custodian account - require_eq!(custodian.owner, program_id, ErrorCode::ConstraintOwner); - // Check usdc mint require_eq!( usdc.key(), From e172c855b44e4148853167f8a19cdada07af0971 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 7 May 2025 15:43:06 +0100 Subject: [PATCH 083/112] reintroduce match statement for additional grace period --- .../src/fallback/processor/execute_order.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index f81c97272..17b176360 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -432,7 +432,12 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // the fast fill will most likely require an additional transaction, so this buffer allows // the best offer participant to perform his duty without the risk of getting slashed by // another executor. - let additional_grace_period = Some(crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD); + let additional_grace_period = match active_auction.target_protocol { + MessageProtocol::Local { .. } => { + crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD.into() + } + _ => None, + }; let DepositPenalty { penalty, From 4fd8ed243489ce8d7da4830dc28f22159031208f Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 7 May 2025 18:23:27 +0100 Subject: [PATCH 084/112] improvements from the call --- .../fallback/processor/close_fast_market_order.rs | 13 ------------- .../src/fallback/processor/execute_order.rs | 8 +------- .../src/fallback/processor/place_initial_offer.rs | 6 ++++++ .../fallback/processor/prepare_order_response.rs | 2 ++ .../matching-engine/src/state/fast_market_order.rs | 4 +++- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 38b44222b..5e50becd4 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -75,19 +75,6 @@ pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { ); } - if fast_market_order_deserialized.close_account_refund_recipient - != close_account_refund_recipient.key() - { - return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( - |e: Error| { - e.with_pubkeys(( - fast_market_order_deserialized.close_account_refund_recipient, - close_account_refund_recipient.key(), - )) - }, - ); - } - // First, get the current lamports value let current_recipient_lamports = **close_account_refund_recipient.lamports.borrow(); diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 17b176360..d6f83fcbb 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -471,7 +471,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .saturating_add(active_auction_info.security_deposit) .saturating_sub(user_reward); - msg!("Security deposit: {}", active_auction_info.security_deposit); let penalized = penalty > 0; @@ -493,7 +492,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { if utils::checked_deserialize_token_account(initial_offer_token_account, &common::USDC_MINT) .is_some() { - msg!("Initial offer token account exists"); if active_auction_best_offer_token_account.key() != initial_offer_token_account.key() { // Pay the auction initiator their fee. let transfer_ix = spl_token::instruction::transfer( @@ -505,10 +503,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { init_auction_fee, ) .unwrap(); - msg!( - "Sending init auction fee {} to initial offer token account", - init_auction_fee - ); + invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; // Because the initial offer token was paid this fee, we account for it here. remaining_custodied_amount = @@ -518,7 +513,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { deposit_and_fee = deposit_and_fee .checked_add(init_auction_fee) .ok_or_else(|| MatchingEngineError::U64Overflow)?; - msg!("New deposit and fee: {}", deposit_and_fee); } } diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 45a37bdae..e42b92846 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -318,6 +318,12 @@ pub fn place_initial_offer_cctp_shim( return Err(MatchingEngineError::InvalidSourceRouter.into()); } + // Check that the vaa emitter chain is equal to the from_endpoints chain + if from_endpoint_account.chain != fast_market_order_zero_copy.vaa_emitter_chain { + msg!("Vaa emitter chain is not equal to the from_endpoints chain"); + return Err(MatchingEngineError::InvalidSourceRouter.into()); + } + // Check that to endpoint chain is equal to the fast_market_order target_chain if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { msg!("To endpoint chain is not equal to the fast_market_order target_chain"); diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 3954ebc31..92e34e97f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -295,12 +295,14 @@ pub fn prepare_order_response_cctp_shim( MatchingEngineError::InvalidTargetRouter ); + // Check that the prepared order response pda is equal to the prepared order response account key require_eq!( prepared_order_response_pda, prepared_order_response.key(), MatchingEngineError::InvalidPda ); + // Check that the prepared custody token pda is equal to the prepared custody token account key require_eq!( prepared_custody_token_pda, prepared_custody_token.key(), diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 41223876c..1b42fcf76 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -29,6 +29,7 @@ pub struct FastMarketOrder { /// The initial auction fee of the fast transfer pub init_auction_fee: u64, /// The redeemer message of the fast transfer + /// NOTE: This value is based on the max redeemer length of 500 bytes that is specified in the token router program. If this changes in the future, this value must be updated. pub redeemer_message: [u8; 512], /// The refund recipient for the creator of the fast market order account pub close_account_refund_recipient: Pubkey, @@ -38,7 +39,8 @@ pub struct FastMarketOrder { pub vaa_sequence: u64, /// The timestamp of the fast transfer vaa pub vaa_timestamp: u32, - /// The vaa nonce, which is not used and can be set to 0 + /// The vaa nonce, which is not used and can be set to 0. + // TODO: Can be taken out. pub vaa_nonce: u32, /// The source chain of the fast transfer vaa (represented as a wormhole chain id) pub vaa_emitter_chain: u16, From 1a7abb6bada907b4b4eabe4a55f1577fd984fe2c Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 7 May 2025 19:39:07 +0100 Subject: [PATCH 085/112] all tests passing --- .../tests/test_scenarios/prepare_order.rs | 8 +++--- .../tests/testing_engine/engine.rs | 2 ++ .../tests/testing_engine/setup.rs | 25 +++++++++++++------ .../tests/testing_engine/state.rs | 4 +++ .../processor/prepare_order_response.rs | 1 + 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs index 1452611f7..e832d8952 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/prepare_order.rs @@ -514,8 +514,8 @@ pub async fn test_prepare_order_response_shim_emitter_chain_mismatch() { vaa_index: 1, expected_error: Some(ExpectedError { instruction_index: 0, - error_code: u32::from(MatchingEngineError::InvalidCctpMessage), - error_string: "Invalid cctp message".to_string(), + error_code: 0, + error_string: "".to_string(), }), ..PrepareOrderResponseInstructionConfig::default() }), @@ -639,8 +639,8 @@ pub async fn test_prepare_order_response_shim_deposit_cctp_nonce_mismatch() { vaa_index: 1, expected_error: Some(ExpectedError { instruction_index: 0, - error_code: u32::from(MatchingEngineError::InvalidCctpMessage), - error_string: "Invalid cctp message".to_string(), + error_code: 0, + error_string: "".to_string(), }), ..PrepareOrderResponseInstructionConfig::default() }), diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index a2d77a863..6b7c28927 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -753,6 +753,7 @@ impl TestingEngine { auction_state: current_state.auction_state().clone(), order_executed: order_executed_state, auction_accounts: auction_accounts.clone(), + order_prepared: current_state.order_prepared().cloned(), } } else { current_state.clone() @@ -818,6 +819,7 @@ impl TestingEngine { auction_state: current_state.auction_state().clone(), order_executed: order_executed_state, auction_accounts: auction_accounts.clone(), + order_prepared: current_state.order_prepared().cloned(), } } else { current_state.clone() diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index e912768e1..6407bbbdb 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -416,18 +416,29 @@ impl TestingContext { match tx_error { BanksClientError::TransactionError(TransactionError::InstructionError( instruction_index, - InstructionError::Custom(error_code), + ref instruction_error, )) => { assert_eq!( instruction_index, expected_error.instruction_index, "Expected error on instruction {}, but got: {:?}", expected_error.instruction_index, tx_error - ); - assert_eq!( - error_code, expected_error.error_code, - "Program returned error code {}, expected {} ({:?})", - error_code, expected_error.error_code, expected_error.error_string - ); + ); + match instruction_error { + InstructionError::Custom(error_code) => { + assert_eq!( + error_code, &expected_error.error_code, + "Program returned error code {}, expected {} ({:?})", + error_code, expected_error.error_code, expected_error.error_string + ); + } + // TODO; Catch custom instruction errors or smth + _ => { + assert_eq!( + 0, expected_error.error_code, + "This is a non custom instruction error, and if expected, error code should be 0" + ); + } + } } _ => { panic!( diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index 5aee594bc..f55037aed 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -145,6 +145,7 @@ pub enum TestingEngineState { auction_state: AuctionState, order_executed: OrderExecutedState, auction_accounts: AuctionAccounts, + order_prepared: Option, }, OrderPrepared { base: BaseState, @@ -337,6 +338,7 @@ impl TestingEngineState { Self::InitialOfferPlaced { order_prepared, .. } => order_prepared.as_ref(), Self::OfferImproved { order_prepared, .. } => order_prepared.as_ref(), Self::FastMarketOrderAccountCreated { order_prepared, .. } => order_prepared.as_ref(), + Self::OrderExecuted { order_prepared, .. } => order_prepared.as_ref(), _ => None, } } @@ -438,6 +440,7 @@ impl TestingEngineState { auction_state: _, // Ignore the current auction state order_executed, auction_accounts, + order_prepared, } => Ok(Self::OrderExecuted { base: base.clone(), initialized: initialized.clone(), @@ -446,6 +449,7 @@ impl TestingEngineState { auction_state: new_auction_state, // Use the new auction state order_executed: order_executed.clone(), auction_accounts: auction_accounts.clone(), + order_prepared: order_prepared.clone(), }), Self::OrderPrepared { diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 92e34e97f..76542fada 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -208,6 +208,7 @@ pub fn prepare_order_response_cctp_shim( let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; + // Load accounts let fast_market_order_account_data = &fast_market_order.data.borrow()[..]; let fast_market_order_zero_copy = From e0f97f9789aed68c3cc0f18e3b9eb9101f3a66e8 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 8 May 2025 17:44:32 +0100 Subject: [PATCH 086/112] [broken] signers not attached properly --- .../tests/shimful/fast_market_order_shim.rs | 81 +++- .../tests/shimful/shims_make_offer.rs | 378 +++++++++++------- .../tests/shimless/initialize.rs | 242 ++++++----- .../test_scenarios/initialize_and_misc.rs | 35 +- .../tests/test_scenarios/make_offer.rs | 29 +- .../tests/testing_engine/config.rs | 33 ++ .../tests/testing_engine/engine.rs | 316 ++++++++------- 7 files changed, 700 insertions(+), 414 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 32a3c48d5..faed01419 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -1,6 +1,11 @@ -use crate::testing_engine::config::ExpectedError; +use crate::testing_engine::config::{ + ExpectedError, InitializeFastMarketOrderShimInstructionConfig, +}; +use crate::testing_engine::state::{ + FastMarketOrderAccountCreatedState, GuardianSetState, TestingEngineState, +}; -use super::verify_shim::GuardianSignatureInfo; +use super::verify_shim::{create_guardian_signatures, GuardianSignatureInfo}; use crate::testing_engine::setup::TestingContext; use crate::utils; use common::messages::FastMarketOrder; @@ -38,27 +43,57 @@ use wormhole_io::TypePrefixedPayload; /// # Asserts /// /// * The expected error, if any, is reached when executing the instruction -pub async fn initialize_fast_market_order_fallback( +pub async fn initialize_fast_market_order_shimful( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - payer_signer: &Rc, - fast_market_order: FastMarketOrderState, - guardian_signature_info: &GuardianSignatureInfo, expected_error: Option<&ExpectedError>, -) { + current_state: &TestingEngineState, + config: &InitializeFastMarketOrderShimInstructionConfig, +) -> TestingEngineState { let program_id = &testing_context.get_matching_engine_program_id(); - let initialize_fast_market_order_ix = initialize_fast_market_order_fallback_instruction( - payer_signer, + let test_vaa_pair = current_state.get_test_vaa_pair(config.vaa_index); + let fast_transfer_vaa = test_vaa_pair.fast_transfer_vaa.clone(); + let fast_market_order = create_fast_market_order_state_from_vaa_data( + &fast_transfer_vaa.vaa_data, + config + .close_account_refund_recipient + .unwrap_or_else(|| testing_context.testing_actors.solvers[0].pubkey()), + ); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let guardian_signature_info = create_guardian_signatures( + &testing_context, + test_context, + &payer_signer, + &fast_transfer_vaa.vaa_data, + &testing_context.get_wormhole_program_id(), + None, + ) + .await + .expect("Failed to create guardian signatures"); + + let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.close_account_refund_recipient.as_ref(), + ], + program_id, + ); + let initialize_fast_market_order_ix = initialize_fast_market_order_shimful_instruction( + &payer_signer, program_id, fast_market_order, - guardian_signature_info, + &guardian_signature_info, ); let transaction = testing_context .create_transaction( test_context, &[initialize_fast_market_order_ix], Some(&payer_signer.pubkey()), - &[payer_signer], + &[&payer_signer], 1000000000, 1000000000, ) @@ -66,6 +101,28 @@ pub async fn initialize_fast_market_order_fallback( testing_context .execute_and_verify_transaction(test_context, transaction, expected_error) .await; + if config.expected_error.is_none() { + TestingEngineState::FastMarketOrderAccountCreated { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().cloned(), + fast_market_order: FastMarketOrderAccountCreatedState { + fast_market_order_address: fast_market_order_account, + fast_market_order_bump, + fast_market_order, + close_account_refund_recipient: fast_market_order.close_account_refund_recipient, + }, + guardian_set_state: GuardianSetState { + guardian_set_address: guardian_signature_info.guardian_set_pubkey, + guardian_signatures_address: guardian_signature_info.guardian_signatures_pubkey, + }, + auction_state: current_state.auction_state().clone(), + auction_accounts: current_state.auction_accounts().cloned(), + order_prepared: current_state.order_prepared().cloned(), + } + } else { + current_state.clone() + } } /// Creates the initialize fast market order fallback instruction @@ -84,7 +141,7 @@ pub async fn initialize_fast_market_order_fallback( /// # Returns /// /// * `Instruction` - The initialize fast market order fallback instruction -fn initialize_fast_market_order_fallback_instruction( +pub fn initialize_fast_market_order_shimful_instruction( payer_signer: &Rc, program_id: &Pubkey, fast_market_order: FastMarketOrderState, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 02dfaa23d..678da7841 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -1,4 +1,6 @@ -use crate::testing_engine::config::{ExpectedError, PlaceInitialOfferInstructionConfig}; +use crate::testing_engine::config::{ + ExpectedError, InstructionConfig, PlaceInitialOfferInstructionConfig, +}; use crate::testing_engine::state::{InitialOfferPlacedState, TestingEngineState}; use crate::utils::auction::AuctionAccounts; @@ -13,7 +15,7 @@ use matching_engine::state::Auction; use solana_program_test::ProgramTestContext; use super::fast_market_order_shim::create_fast_market_order_state_from_vaa_data; -use solana_sdk::{pubkey::Pubkey, signer::Signer, transaction::Transaction}; +use solana_sdk::{pubkey::Pubkey, signer::Signer}; /// Places an initial offer using the fallback program. The vaa is constructed from a passed in PostedVaaData struct. The nonce is forced to 0. /// @@ -36,144 +38,73 @@ use solana_sdk::{pubkey::Pubkey, signer::Signer, transaction::Transaction}; /// /// * The expected error is reached /// * If successful, the solver's USDC balance should decrease by the offer price -pub async fn place_initial_offer_fallback( +pub async fn place_initial_offer_shimful( testing_context: &TestingContext, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, expected_error: Option<&ExpectedError>, -) -> Option { +) -> TestingEngineState { let payer_signer = config .payer_signer .clone() .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let close_account_refund_recipient = current_state - .fast_market_order() - .unwrap() - .close_account_refund_recipient; - let fast_market_order_address = match &config.fast_market_order_address { - Some(fast_market_order_address) => *fast_market_order_address, - None => { - current_state - .fast_market_order() - .expect("Fast market order is not created") - .fast_market_order_address - } - }; - let auction_config_address = current_state.auction_config_address().unwrap(); - let custodian_address = current_state.custodian_address().unwrap(); - let program_id = testing_context.get_matching_engine_program_id(); - let fast_transfer_vaa = ¤t_state - .base() - .vaas - .get(config.test_vaa_pair_index) - .expect("Failed to get vaa pair") - .fast_transfer_vaa; - let vaa_data = fast_transfer_vaa.get_vaa_data(); - let fast_market_order = - create_fast_market_order_state_from_vaa_data(vaa_data, close_account_refund_recipient); - let offer_price = config.offer_price; - let actor_enum = config.actor; - let offer_actor = config.actor.get_actor(&testing_context.testing_actors); - let offer_token = match &config.custom_accounts { - Some(custom_accounts) => match custom_accounts.offer_token_address { - Some(offer_token_address) => offer_token_address, - None => offer_actor - .token_account_address(&config.spl_token_enum) - .unwrap(), - }, - None => offer_actor - .token_account_address(&config.spl_token_enum) - .unwrap(), - }; - let auction_address = Pubkey::find_program_address( - &[Auction::SEED_PREFIX, &fast_market_order.digest()], - &program_id, - ) - .0; - let auction_custody_token_address = Pubkey::find_program_address( - &[ - matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction_address.as_ref(), - ], - &program_id, - ) - .0; - - // Approve the transfer authority - let transfer_authority = Pubkey::find_program_address( - &[ - common::TRANSFER_AUTHORITY_SEED_PREFIX, - &auction_address.to_bytes(), - &offer_price.to_be_bytes(), - ], - &program_id, - ) - .0; + let place_initial_offer_accounts = + PlaceInitialOfferShimfulAccounts::new(testing_context, current_state, config); - offer_actor - .approve_spl_token( - test_context, - &transfer_authority, - 420_000__000_000, - &config.spl_token_enum, - ) - .await; + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); let actor_usdc_balance_before = offer_actor .get_token_account_balance(test_context, &config.spl_token_enum) .await; - let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData { offer_price }; - - let (from_router_endpoint, to_router_endpoint) = - config.get_from_and_to_router_endpoints(current_state); - - let usdc_mint_address = match &config.custom_accounts { - Some(custom_accounts) => match custom_accounts.mint_address { - Some(usdc_mint_address) => usdc_mint_address, - None => testing_context.get_usdc_mint_address(), - }, - None => testing_context.get_usdc_mint_address(), - }; - - let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { - signer: &payer_signer.pubkey(), - transfer_authority: &transfer_authority, - custodian: &custodian_address, - auction_config: &auction_config_address, - from_endpoint: &from_router_endpoint, - to_endpoint: &to_router_endpoint, - fast_market_order: &fast_market_order_address, - auction: &auction_address, - offer_token: &offer_token, - auction_custody_token: &auction_custody_token_address, - usdc: &usdc_mint_address, - system_program: &solana_program::system_program::ID, - token_program: &anchor_spl::token::spl_token::ID, - }; - let place_initial_offer_ix = PlaceInitialOfferCctpShimFallback { - program_id: &program_id, - accounts: place_initial_offer_ix_accounts, - data: place_initial_offer_ix_data, - } - .instruction(); - - let recent_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .unwrap(); + let place_initial_offer_ix = place_initial_offer_shimful_instruction( + testing_context, + test_context, + current_state, + config, + ) + .await; - let transaction = Transaction::new_signed_with_payer( - &[place_initial_offer_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - recent_blockhash, - ); + let transaction = testing_context + .create_transaction( + test_context, + &[place_initial_offer_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + 1000000000, + 1000000000, + ) + .await; testing_context .execute_and_verify_transaction(test_context, transaction, expected_error) .await; + evaluate_place_initial_offer_shimful_state( + testing_context, + test_context, + current_state, + config, + actor_usdc_balance_before, + &place_initial_offer_accounts, + ) + .await +} + +pub async fn evaluate_place_initial_offer_shimful_state( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + actor_usdc_balance_before: u64, + place_initial_offer_accounts: &PlaceInitialOfferShimfulAccounts, +) -> TestingEngineState { + let expected_error = config.expected_error(); + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); if expected_error.is_none() { let actor_usdc_balance_after = offer_actor .get_token_account_balance(test_context, &config.spl_token_enum) @@ -183,42 +114,217 @@ pub async fn place_initial_offer_fallback( "Solver USDC balance should have decreased" ); let new_active_auction_state = utils::auction::ActiveAuctionState { - auction_address, - auction_custody_token_address, - auction_config_address, + auction_address: place_initial_offer_accounts.auction, + auction_custody_token_address: place_initial_offer_accounts.auction_custody_token, + auction_config_address: place_initial_offer_accounts.auction_config, initial_offer: utils::auction::AuctionOffer { - actor: actor_enum, + actor: config.actor, participant: payer_signer.pubkey(), - offer_token, - offer_price, + offer_token: place_initial_offer_accounts.offer_token, + offer_price: config.offer_price, }, best_offer: utils::auction::AuctionOffer { - actor: actor_enum, + actor: config.actor, participant: payer_signer.pubkey(), - offer_token, - offer_price, + offer_token: place_initial_offer_accounts.offer_token, + offer_price: config.offer_price, }, spl_token_enum: config.spl_token_enum.clone(), }; let new_auction_state = utils::auction::AuctionState::Active(Box::new(new_active_auction_state)); - Some(InitialOfferPlacedState { + let initial_offer_placed_state = InitialOfferPlacedState { auction_state: new_auction_state, auction_accounts: AuctionAccounts::new( - Some(fast_transfer_vaa.get_vaa_pubkey()), + None, offer_actor.clone(), current_state.close_account_refund_recipient(), - auction_config_address, + place_initial_offer_accounts.auction_config, ¤t_state .router_endpoints() .expect("Router endpoints are not created") .endpoints, - custodian_address, + place_initial_offer_accounts.custodian, config.spl_token_enum.clone(), current_state.base().transfer_direction, ), - }) - } else { - None + }; + let active_auction_state = initial_offer_placed_state + .auction_state + .get_active_auction() + .unwrap(); + active_auction_state + .verify_auction(&testing_context, test_context) + .await + .expect("Could not verify auction"); + let auction_accounts = initial_offer_placed_state.auction_accounts; + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: initial_offer_placed_state.auction_state, + auction_accounts, + order_prepared: current_state.order_prepared().cloned(), + }; + } + current_state.clone() +} + +pub async fn place_initial_offer_shimful_instruction( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, +) -> solana_program::instruction::Instruction { + let place_initial_offer_accounts = + PlaceInitialOfferShimfulAccounts::new(testing_context, current_state, config); + + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); + + offer_actor + .approve_spl_token( + test_context, + &place_initial_offer_accounts.transfer_authority, + 420_000__000_000, + &config.spl_token_enum, + ) + .await; + + let place_initial_offer_ix_data = PlaceInitialOfferCctpShimFallbackData { + offer_price: config.offer_price, + }; + + let place_initial_offer_ix_accounts = PlaceInitialOfferCctpShimFallbackAccounts { + signer: &place_initial_offer_accounts.signer, + transfer_authority: &place_initial_offer_accounts.transfer_authority, + custodian: &place_initial_offer_accounts.custodian, + auction_config: &place_initial_offer_accounts.auction_config, + from_endpoint: &place_initial_offer_accounts.from_endpoint, + to_endpoint: &place_initial_offer_accounts.to_endpoint, + fast_market_order: &place_initial_offer_accounts.fast_market_order, + auction: &place_initial_offer_accounts.auction, + offer_token: &place_initial_offer_accounts.offer_token, + auction_custody_token: &place_initial_offer_accounts.auction_custody_token, + usdc: &place_initial_offer_accounts.usdc, + system_program: &place_initial_offer_accounts.system_program, + token_program: &place_initial_offer_accounts.token_program, + }; + PlaceInitialOfferCctpShimFallback { + program_id: &testing_context.get_matching_engine_program_id(), + accounts: place_initial_offer_ix_accounts, + data: place_initial_offer_ix_data, + } + .instruction() +} + +pub struct PlaceInitialOfferShimfulAccounts { + pub signer: Pubkey, + pub transfer_authority: Pubkey, + pub custodian: Pubkey, + pub auction_config: Pubkey, + pub from_endpoint: Pubkey, + pub to_endpoint: Pubkey, + pub fast_market_order: Pubkey, + pub auction: Pubkey, + pub offer_token: Pubkey, + pub auction_custody_token: Pubkey, + pub usdc: Pubkey, + pub system_program: Pubkey, + pub token_program: Pubkey, +} + +impl PlaceInitialOfferShimfulAccounts { + pub fn new( + testing_context: &TestingContext, + current_state: &TestingEngineState, + config: &PlaceInitialOfferInstructionConfig, + ) -> Self { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let close_account_refund_recipient = current_state + .fast_market_order() + .unwrap() + .close_account_refund_recipient; + let fast_market_order = match &config.fast_market_order_address { + Some(fast_market_order_address) => *fast_market_order_address, + None => { + current_state + .fast_market_order() + .expect("Fast market order is not created") + .fast_market_order_address + } + }; + let auction_config = current_state.auction_config_address().unwrap(); + let custodian = current_state.custodian_address().unwrap(); + let program_id = testing_context.get_matching_engine_program_id(); + let fast_transfer_vaa = ¤t_state + .base() + .vaas + .get(config.test_vaa_pair_index) + .expect("Failed to get vaa pair") + .fast_transfer_vaa; + let vaa_data = fast_transfer_vaa.get_vaa_data(); + let fast_market_order_state = + create_fast_market_order_state_from_vaa_data(vaa_data, close_account_refund_recipient); + let offer_actor = config.actor.get_actor(&testing_context.testing_actors); + let offer_token = match &config.custom_accounts { + Some(custom_accounts) => match custom_accounts.offer_token_address { + Some(offer_token_address) => offer_token_address, + None => offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(), + }, + None => offer_actor + .token_account_address(&config.spl_token_enum) + .unwrap(), + }; + let auction = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, &fast_market_order_state.digest()], + &program_id, + ) + .0; + let auction_custody_token = Pubkey::find_program_address( + &[ + matching_engine::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + auction.as_ref(), + ], + &program_id, + ) + .0; + let transfer_authority = Pubkey::find_program_address( + &[ + common::TRANSFER_AUTHORITY_SEED_PREFIX, + &auction.to_bytes(), + &config.offer_price.to_be_bytes(), + ], + &program_id, + ) + .0; + let (from_endpoint, to_endpoint) = config.get_from_and_to_router_endpoints(current_state); + let usdc = match &config.custom_accounts { + Some(custom_accounts) => match custom_accounts.mint_address { + Some(usdc_mint_address) => usdc_mint_address, + None => testing_context.get_usdc_mint_address(), + }, + None => testing_context.get_usdc_mint_address(), + }; + Self { + signer: payer_signer.pubkey(), + transfer_authority, + custodian, + auction_config, + from_endpoint, + to_endpoint, + fast_market_order, + auction, + offer_token, + auction_custody_token, + usdc, + system_program: solana_program::system_program::ID, + token_program: anchor_spl::token::spl_token::ID, + } } } diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 4518f805b..9e1fa6871 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -1,9 +1,6 @@ use solana_program_test::ProgramTestContext; use solana_sdk::{ - instruction::Instruction, - pubkey::Pubkey, - signature::{Keypair, Signer}, - transaction::{Transaction, VersionedTransaction}, + instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::VersionedTransaction, }; use anchor_lang::AccountDeserialize; @@ -11,7 +8,10 @@ use anchor_spl::{associated_token::spl_associated_token_account, token::spl_toke use solana_program::{bpf_loader_upgradeable, system_program}; use crate::{ - testing_engine::config::{ExpectedError, ExpectedLog}, + testing_engine::{ + config::{InitializeInstructionConfig, InstructionConfig}, + state::{InitializedState, TestingEngineState}, + }, utils::token_account::SplTokenEnum, }; @@ -23,27 +23,44 @@ use matching_engine::{ InitializeArgs, }; +#[derive(Clone)] pub struct InitializeAddresses { pub custodian_address: Pubkey, pub auction_config_address: Pubkey, pub cctp_mint_recipient: Pubkey, } -pub struct InitializeFixture { - pub custodian: Custodian, - pub addresses: InitializeAddresses, -} +impl InitializeAddresses { + pub fn create( + testing_context: &TestingContext, + auction_parameters_config: &AuctionParametersConfig, + ) -> Self { + let program_id = testing_context.get_matching_engine_program_id(); + let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); + let (custodian, _custodian_bump) = + Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); -impl InitializeFixture { - pub fn get_custodian_address(&self) -> Pubkey { - self.addresses.custodian_address - } + let (auction_config, _auction_config_bump) = Pubkey::find_program_address( + &[ + AuctionConfig::SEED_PREFIX, + &auction_parameters_config.config_id.to_be_bytes(), + ], + &program_id, + ); - pub fn get_auction_config_address(&self) -> Pubkey { - self.addresses.auction_config_address + Self { + custodian_address: custodian, + auction_config_address: auction_config, + cctp_mint_recipient, + } } } +pub struct InitializeFixture { + pub custodian: Custodian, + pub addresses: InitializeAddresses, +} + #[derive(Debug, PartialEq, Eq)] struct TestCustodian { owner: Pubkey, @@ -125,8 +142,8 @@ impl Default for AuctionParametersConfig { } } -impl From for AuctionParameters { - fn from(val: AuctionParametersConfig) -> Self { +impl From<&AuctionParametersConfig> for AuctionParameters { + fn from(val: &AuctionParametersConfig) -> Self { AuctionParameters { user_penalty_reward_bps: val.user_penalty_reward_bps, initial_penalty_bps: val.initial_penalty_bps, @@ -143,25 +160,108 @@ impl From for AuctionParameters { pub async fn initialize_program( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - auction_parameters_config: AuctionParametersConfig, - payer_signer: &Keypair, - expected_error: Option<&ExpectedError>, - expected_log_messages: Option<&Vec>, -) -> Option { - let program_id = testing_context.get_matching_engine_program_id(); - let usdc_mint_address = testing_context.get_usdc_mint_address(); - let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); - let (custodian, _custodian_bump) = - Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); + initial_state: &TestingEngineState, + config: &InitializeInstructionConfig, +) -> TestingEngineState { + let auction_parameters_config = config.auction_parameters_config.clone(); + let expected_error = config.expected_error(); + let expected_log_messages = config.expected_log_messages(); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + // Create the initialize addresses + let initialize_addresses = + InitializeAddresses::create(testing_context, &auction_parameters_config); + // Create the initialize instruction + let instruction = initialize_program_instruction(testing_context, &auction_parameters_config); + // Create and sign transaction + let transaction = testing_context + .create_transaction( + test_context, + &[instruction], + Some(&payer_signer.pubkey()), + &[&payer_signer], + 1000000000, + 1000000000, + ) + .await; + // Process transaction + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + + if let Some(expected_log_messages) = expected_log_messages { + // Recreate the instruction + let instruction = + initialize_program_instruction(testing_context, &auction_parameters_config); + let transaction = testing_context + .create_transaction( + test_context, + &[instruction], + Some(&payer_signer.pubkey()), + &[&payer_signer,], + 1000000000, + 1000000000, + ) + .await; + let versioned_transaction = VersionedTransaction::from(transaction); + + // Simulate and verify logs + testing_context + .simulate_and_verify_logs(test_context, versioned_transaction, expected_log_messages) + .await + .expect("Failed to verify logs"); + } + + if expected_error.is_none() { + // Verify the results + let custodian_account = test_context + .banks_client + .get_account(initialize_addresses.custodian_address) + .await + .expect("Failed to get custodian account") + .expect("Custodian account not found"); + + let custodian = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); + let initialize_fixture = InitializeFixture { + custodian, + addresses: initialize_addresses.clone(), + }; + let testing_actors = &testing_context.testing_actors; + let owner = testing_actors.owner.pubkey(); + let owner_assistant = testing_actors.owner_assistant.pubkey(); + let fee_recipient_token = testing_actors + .fee_recipient + .token_account_address(&SplTokenEnum::Usdc) + .unwrap(); - let (auction_config, _auction_config_bump) = Pubkey::find_program_address( - &[ - AuctionConfig::SEED_PREFIX, - &auction_parameters_config.config_id.to_be_bytes(), - ], - &program_id, - ); + initialize_fixture.verify_custodian(owner, owner_assistant, fee_recipient_token); + TestingEngineState::Initialized { + base: initial_state.base().clone(), + initialized: InitializedState { + auction_config_address: initialize_addresses.auction_config_address, + custodian_address: initialize_addresses.custodian_address, + }, + } + } else { + initial_state.clone() + } +} +pub fn initialize_program_instruction( + testing_context: &TestingContext, + auction_parameters_config: &AuctionParametersConfig, +) -> Instruction { + let program_id = testing_context.get_matching_engine_program_id(); + let usdc_mint_address = testing_context.get_usdc_mint_address(); + let initialize_addresses = + InitializeAddresses::create(testing_context, &auction_parameters_config); + let InitializeAddresses { + custodian_address: custodian, + auction_config_address: auction_config, + cctp_mint_recipient, + } = initialize_addresses; // Create AuctionParameters let auction_params: AuctionParameters = auction_parameters_config.into(); @@ -196,81 +296,9 @@ pub async fn initialize_program( }; // Create the instruction - let instruction = Instruction { + Instruction { program_id, accounts: accounts.to_account_metas(None), data: ix_data.data(), - }; - // Create and sign transaction - let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer_signer.pubkey())); - let new_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .expect("Could not get new blockhash"); - transaction.sign( - &[ - &payer_signer, - &testing_context.testing_actors.owner.keypair().as_ref(), - ], - new_blockhash, - ); - - // Process transaction - let versioned_transaction = VersionedTransaction::from(transaction); - testing_context - .execute_and_verify_transaction(test_context, versioned_transaction, expected_error) - .await; - - if let Some(expected_log_messages) = expected_log_messages { - // Recreate the instruction - let instruction = Instruction { - program_id, - accounts: accounts.to_account_metas(None), - data: ix_data.data(), - }; - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&test_context.payer.pubkey())); - let new_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .expect("Could not get new blockhash"); - transaction.sign( - &[ - &test_context.payer, - &testing_context.testing_actors.owner.keypair(), - ], - new_blockhash, - ); - let versioned_transaction = VersionedTransaction::from(transaction); - - // Simulate and verify logs - testing_context - .simulate_and_verify_logs(test_context, versioned_transaction, expected_log_messages) - .await - .expect("Failed to verify logs"); - } - - if expected_error.is_none() { - // Verify the results - let custodian_account = test_context - .banks_client - .get_account(custodian) - .await - .expect("Failed to get custodian account") - .expect("Custodian account not found"); - - let custodian_data = - Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); - let initialize_addresses = InitializeAddresses { - custodian_address: custodian, - auction_config_address: auction_config, - cctp_mint_recipient, - }; - Some(InitializeFixture { - custodian: custodian_data, - addresses: initialize_addresses, - }) - } else { - None } } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs index 8d9ddb993..3c520e392 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs @@ -23,7 +23,7 @@ use anchor_lang::AccountDeserialize; use anchor_spl::token::TokenAccount; use matching_engine::ID as PROGRAM_ID; use shimful::post_message::set_up_post_message_transaction_test; -use shimless::initialize::{initialize_program, AuctionParametersConfig}; +use shimless::initialize::initialize_program; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; use testing_engine::config::*; @@ -116,25 +116,30 @@ pub async fn test_local_token_router_endpoint_creation() { None, ) .await; - let payer_signer = testing_context.testing_actors.payer_signer.clone(); - let initialize_fixture = initialize_program( - &testing_context, + let testing_engine = TestingEngine::new(testing_context).await; + let config = InitializeInstructionConfig::default(); + let initial_state = testing_engine.create_initial_state(); + let payer_signer = testing_engine + .testing_context + .testing_actors + .payer_signer + .clone(); + let initialize_state = initialize_program( + &testing_engine.testing_context, &mut test_context, - AuctionParametersConfig::default(), - &payer_signer, - None, // No expected error - None, // No expected log messages + &initial_state, + &config, ) - .await - .expect("Failed to initialize program"); - let payer_signer = testing_context.testing_actors.payer_signer.clone(); + .await; + let custodian = initialize_state.auction_accounts().unwrap().custodian; + let owner = &testing_engine.testing_context.testing_actors.owner; let _local_token_router_endpoint = add_local_router_endpoint_ix( - &testing_context, + &testing_engine.testing_context, &mut test_context, &payer_signer, - testing_context.testing_actors.owner.pubkey(), - initialize_fixture.get_custodian_address(), - testing_context.testing_actors.owner.keypair().as_ref(), + owner.pubkey(), + custodian, + owner.keypair().as_ref(), ) .await; } diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index 8db945b94..fa7dee61c 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -21,6 +21,7 @@ use crate::testing_engine; use crate::testing_engine::config::{ InitializeInstructionConfig, PlaceInitialOfferInstructionConfig, }; +use crate::testing_engine::engine::CombinationTrigger; use crate::testing_engine::state::TestingEngineState; use crate::utils; use crate::utils::auction::compare_auctions; @@ -73,7 +74,7 @@ const TRANSFER_DIRECTION: TransferDirection = TransferDirection::FromEthereumToA /// Test that the place initial offer shim instruction works correctly from arbitrum to ethereum #[tokio::test] -pub async fn test_place_initial_offer_shim() { +pub async fn test_place_initial_offer_shimful() { let config = PlaceInitialOfferInstructionConfig::default(); let (final_state, _, _) = Box::pin(place_initial_offer_shim(config, None, TRANSFER_DIRECTION)).await; @@ -182,6 +183,32 @@ pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { .await; } +/// Test that place initial offer and create fast market order can be done in one transaction +#[tokio::test] +pub async fn test_place_initial_offer_and_create_fast_market_order_in_one_transaction() { + let config = Box::new(CombinedInstructionConfig::create_fast_market_order_and_place_initial_offer()); + let vaa_args = + vec![VaaArgs { + post_vaa: false, + ..VaaArgs::default() + }]; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + TransferDirection::FromArbitrumToEthereum, + Some(vaa_args), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + let initialize_instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ]; + let initial_state = testing_engine.execute(&mut test_context, initialize_instruction_triggers, None).await; + let instruction_triggers = vec![CombinationTrigger::CreateFastMarketOrderAndPlaceInitialOffer(config)]; + testing_engine.execute(&mut test_context, instruction_triggers, Some(initial_state)).await; +} /* Sad path tests section diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 942b2aa33..9d1284b26 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -619,3 +619,36 @@ pub struct BalanceChange { pub usdc: i32, pub usdt: i32, } + +pub struct CombinedInstructionConfig { + pub create_fast_market_order_config: Option, + pub place_initial_offer_config: Option, + pub execute_order_config: Option, + pub settle_auction_config: Option, + pub close_fast_market_order_config: Option, + pub improve_offer_config: Option, +} + +impl Default for CombinedInstructionConfig { + fn default() -> Self { + Self { + create_fast_market_order_config: None, + place_initial_offer_config: None, + execute_order_config: None, + settle_auction_config: None, + close_fast_market_order_config: None, + improve_offer_config: None, + } + } +} + +impl CombinedInstructionConfig { + pub fn create_fast_market_order_and_place_initial_offer( + ) -> Self { + Self { + create_fast_market_order_config: Some(InitializeFastMarketOrderShimInstructionConfig::default()), + place_initial_offer_config: Some(PlaceInitialOfferInstructionConfig::default()), + ..Default::default() + } + } +} \ No newline at end of file diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 6b7c28927..c141742c5 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -33,10 +33,16 @@ use super::setup::TestingContext; use super::{config::*, state::*}; use crate::shimful; use crate::shimful::fast_market_order_shim::{ - create_fast_market_order_state_from_vaa_data, initialize_fast_market_order_fallback, + create_fast_market_order_state_from_vaa_data, initialize_fast_market_order_shimful, + initialize_fast_market_order_shimful_instruction, +}; +use crate::shimful::shims_make_offer::{ + evaluate_place_initial_offer_shimful_state, place_initial_offer_shimful_instruction, + PlaceInitialOfferShimfulAccounts, }; use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; +use crate::shimless::initialize::initialize_program; use crate::testing_engine::setup::ShimMode; use crate::utils::auction::AuctionState; use crate::utils::token_account::SplTokenEnum; @@ -67,9 +73,14 @@ pub enum VerificationTrigger { VerifyBalances(Box), } +pub enum CombinationTrigger { + CreateFastMarketOrderAndPlaceInitialOffer(Box), +} + pub enum ExecutionTrigger { Instruction(Box), Verification(Box), + CombinationTrigger(Box), } impl From for ExecutionTrigger { @@ -84,6 +95,12 @@ impl From for ExecutionTrigger { } } +impl From for ExecutionTrigger { + fn from(trigger: CombinationTrigger) -> Self { + ExecutionTrigger::CombinationTrigger(Box::new(trigger)) + } +} + pub struct ExecutionChain(Vec); impl Deref for ExecutionChain { @@ -129,6 +146,12 @@ impl From> for ExecutionChain { } } +impl From> for ExecutionChain { + fn from(triggers: Vec) -> Self { + Self(triggers.into_iter().map(|trigger| trigger.into()).collect()) + } +} + impl InstructionTrigger { pub fn is_shim(&self) -> bool { matches!( @@ -312,7 +335,7 @@ impl TestingEngine { .await } InstructionTrigger::PlaceInitialOfferShim(ref config) => { - self.place_initial_offer_shim(test_context, current_state, config) + self.place_initial_offer_shimful(test_context, current_state, config) .await } InstructionTrigger::ImproveOfferShimless(ref config) => { @@ -350,6 +373,21 @@ impl TestingEngine { .await } }, + ExecutionTrigger::CombinationTrigger(trigger) => match **trigger { + CombinationTrigger::CreateFastMarketOrderAndPlaceInitialOffer(ref configs) => { + let create_fast_market_order_config = + configs.create_fast_market_order_config.as_ref().unwrap(); + let place_initial_offer_config = + configs.place_initial_offer_config.as_ref().unwrap(); + self.create_fast_market_order_and_place_initial_offer( + test_context, + current_state, + create_fast_market_order_config, + place_initial_offer_config, + ) + .await + } + }, } } @@ -376,54 +414,7 @@ impl TestingEngine { initial_state: &TestingEngineState, config: &InitializeInstructionConfig, ) -> TestingEngineState { - let auction_parameters_config = config.auction_parameters_config.clone(); - let expected_error = config.expected_error(); - let expected_log_messages = config.expected_log_messages(); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - let (result, owner_pubkey, owner_assistant_pubkey, fee_recipient_token_account) = { - let result = shimless::initialize::initialize_program( - &self.testing_context, - test_context, - auction_parameters_config, - &payer_signer, - expected_error, - expected_log_messages, - ) - .await; - - let testing_actors = &self.testing_context.testing_actors; - ( - result, - testing_actors.owner.pubkey(), - testing_actors.owner_assistant.pubkey(), - testing_actors - .fee_recipient - .token_account_address(&SplTokenEnum::Usdc) - .unwrap(), - ) - }; - - if expected_error.is_none() { - let initialize_fixture = result.expect("Failed to initialize program"); - initialize_fixture.verify_custodian( - owner_pubkey, - owner_assistant_pubkey, - fee_recipient_token_account, - ); - - let auction_config_address = initialize_fixture.get_auction_config_address(); - return TestingEngineState::Initialized { - base: initial_state.base().clone(), - initialized: InitializedState { - auction_config_address, - custodian_address: initialize_fixture.get_custodian_address(), - }, - }; - } - initial_state.clone() + initialize_program(&self.testing_context, test_context, initial_state, config).await } /// Instruction trigger function for creating cctp router endpoints @@ -470,71 +461,14 @@ impl TestingEngine { current_state: &TestingEngineState, config: &InitializeFastMarketOrderShimInstructionConfig, ) -> TestingEngineState { - let test_vaa_pair = current_state.get_test_vaa_pair(config.vaa_index); - let fast_transfer_vaa = test_vaa_pair.fast_transfer_vaa.clone(); - let fast_market_order = create_fast_market_order_state_from_vaa_data( - &fast_transfer_vaa.vaa_data, - config - .close_account_refund_recipient - .unwrap_or_else(|| self.testing_context.testing_actors.solvers[0].pubkey()), - ); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - let guardian_signature_info = create_guardian_signatures( - &self.testing_context, - test_context, - &payer_signer, - &fast_transfer_vaa.vaa_data, - &self.testing_context.get_wormhole_program_id(), - None, - ) - .await - .expect("Failed to create guardian signatures"); - - let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( - &[ - FastMarketOrder::SEED_PREFIX, - &fast_market_order.digest(), - &fast_market_order.close_account_refund_recipient.as_ref(), - ], - &self.testing_context.get_matching_engine_program_id(), - ); - - initialize_fast_market_order_fallback( + initialize_fast_market_order_shimful( &self.testing_context, test_context, - &payer_signer, - fast_market_order, - &guardian_signature_info, config.expected_error(), + current_state, + config, ) - .await; - - if config.expected_error.is_none() { - TestingEngineState::FastMarketOrderAccountCreated { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().cloned(), - fast_market_order: FastMarketOrderAccountCreatedState { - fast_market_order_address: fast_market_order_account, - fast_market_order_bump, - fast_market_order, - close_account_refund_recipient: fast_market_order - .close_account_refund_recipient, - }, - guardian_set_state: GuardianSetState { - guardian_set_address: guardian_signature_info.guardian_set_pubkey, - guardian_signatures_address: guardian_signature_info.guardian_signatures_pubkey, - }, - auction_state: current_state.auction_state().clone(), - auction_accounts: current_state.auction_accounts().cloned(), - order_prepared: current_state.order_prepared().cloned(), - } - } else { - current_state.clone() - } + .await } /// Instruction trigger function for pausing the custodian @@ -681,43 +615,20 @@ impl TestingEngine { } /// Instruction trigger function for placing an initial offer - async fn place_initial_offer_shim( + async fn place_initial_offer_shimful( &self, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, ) -> TestingEngineState { - let place_initial_offer_shim_fixture = - shimful::shims_make_offer::place_initial_offer_fallback( - &self.testing_context, - test_context, - current_state, - config, - config.expected_error(), - ) - .await; - if config.expected_error.is_none() { - let initial_offer_placed_state = place_initial_offer_shim_fixture.unwrap(); - let active_auction_state = initial_offer_placed_state - .auction_state - .get_active_auction() - .unwrap(); - active_auction_state - .verify_auction(&self.testing_context, test_context) - .await - .expect("Could not verify auction"); - let auction_accounts = initial_offer_placed_state.auction_accounts; - return TestingEngineState::InitialOfferPlaced { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: initial_offer_placed_state.auction_state, - auction_accounts, - order_prepared: current_state.order_prepared().cloned(), - }; - } - current_state.clone() + shimful::shims_make_offer::place_initial_offer_shimful( + &self.testing_context, + test_context, + current_state, + config, + config.expected_error(), + ) + .await } /// Instruction trigger function for executing an order @@ -1012,6 +923,125 @@ impl TestingEngine { current_state.clone() } + async fn create_fast_market_order_and_place_initial_offer( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + create_fast_market_order_config: &InitializeFastMarketOrderShimInstructionConfig, + place_initial_offer_config: &PlaceInitialOfferInstructionConfig, + ) -> TestingEngineState { + let program_id = &self.testing_context.get_matching_engine_program_id(); + let test_vaa_pair = + current_state.get_test_vaa_pair(create_fast_market_order_config.vaa_index); + let fast_transfer_vaa = test_vaa_pair.fast_transfer_vaa.clone(); + let fast_market_order = create_fast_market_order_state_from_vaa_data( + &fast_transfer_vaa.vaa_data, + create_fast_market_order_config + .close_account_refund_recipient + .unwrap_or_else(|| self.testing_context.testing_actors.solvers[0].pubkey()), + ); + let create_fast_market_order_payer_signer = create_fast_market_order_config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); + println!("Got here 0"); + let guardian_signature_info = create_guardian_signatures( + &self.testing_context, + test_context, + &create_fast_market_order_payer_signer, + &fast_transfer_vaa.vaa_data, + &self.testing_context.get_wormhole_program_id(), + None, + ) + .await + .expect("Failed to create guardian signatures"); + let (fast_market_order_account, fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrder::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.close_account_refund_recipient.as_ref(), + ], + program_id, + ); + let create_fast_market_order_instruction = initialize_fast_market_order_shimful_instruction( + &create_fast_market_order_payer_signer, + program_id, + fast_market_order, + &guardian_signature_info, + ); + + let place_initial_offer_instruction = place_initial_offer_shimful_instruction( + &self.testing_context, + test_context, + current_state, + place_initial_offer_config, + ) + .await; + let place_initial_offer_payer_signer = place_initial_offer_config + .payer_signer + .clone() + .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); + println!("Got here 1"); + let transaction = self + .testing_context + .create_transaction( + test_context, + &[ + create_fast_market_order_instruction, + place_initial_offer_instruction, + ], + Some(&place_initial_offer_payer_signer.pubkey()), + &[ + &place_initial_offer_payer_signer, + ], + 1000000000, + 1000000000, + ) + .await; + println!("Got here 2"); + let actor_usdc_balance_before = place_initial_offer_config + .actor + .get_actor(&self.testing_context.testing_actors) + .get_token_account_balance(test_context, &place_initial_offer_config.spl_token_enum) + .await; + let place_initial_offer_accounts = &PlaceInitialOfferShimfulAccounts::new( + &self.testing_context, + current_state, + place_initial_offer_config, + ); + self.testing_context + .execute_and_verify_transaction(test_context, transaction, None) + .await; + println!("Got here 3"); + let fast_market_order_created_state = TestingEngineState::FastMarketOrderAccountCreated { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().cloned(), + fast_market_order: FastMarketOrderAccountCreatedState { + fast_market_order_address: fast_market_order_account, + fast_market_order_bump, + fast_market_order, + close_account_refund_recipient: fast_market_order.close_account_refund_recipient, + }, + guardian_set_state: GuardianSetState { + guardian_set_address: guardian_signature_info.guardian_set_pubkey, + guardian_signatures_address: guardian_signature_info.guardian_signatures_pubkey, + }, + auction_state: current_state.auction_state().clone(), + auction_accounts: current_state.auction_accounts().cloned(), + order_prepared: current_state.order_prepared().cloned(), + }; + evaluate_place_initial_offer_shimful_state( + &self.testing_context, + test_context, + &fast_market_order_created_state, + place_initial_offer_config, + actor_usdc_balance_before, + place_initial_offer_accounts, + ) + .await + } + pub async fn make_auction_passed_penalty_period( &self, test_context: &mut ProgramTestContext, From 940c716346005ee8a03a8ec6bc42e3a8b414ac4b Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 8 May 2025 19:15:33 +0100 Subject: [PATCH 087/112] [working] instruction added to check that create fast market order and place initial offer can be done in the same tx --- .../tests/shimful/shims_make_offer.rs | 12 +++-- .../tests/shimless/initialize.rs | 4 +- .../test_scenarios/initialize_and_misc.rs | 2 +- .../tests/test_scenarios/make_offer.rs | 46 ++++++++++++------- .../tests/testing_engine/config.rs | 36 +++++++++++++-- .../tests/testing_engine/engine.rs | 4 -- 6 files changed, 71 insertions(+), 33 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 678da7841..0f12af2a2 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -76,7 +76,6 @@ pub async fn place_initial_offer_shimful( 1000000000, ) .await; - testing_context .execute_and_verify_transaction(test_context, transaction, expected_error) .await; @@ -244,10 +243,13 @@ impl PlaceInitialOfferShimfulAccounts { .payer_signer .clone() .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let close_account_refund_recipient = current_state - .fast_market_order() - .unwrap() - .close_account_refund_recipient; + let close_account_refund_recipient = + config.close_account_refund_recipient.unwrap_or_else(|| { + current_state + .fast_market_order() + .unwrap() + .close_account_refund_recipient + }); let fast_market_order = match &config.fast_market_order_address { Some(fast_market_order_address) => *fast_market_order_address, None => { diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 9e1fa6871..5a57fef95 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -181,7 +181,7 @@ pub async fn initialize_program( test_context, &[instruction], Some(&payer_signer.pubkey()), - &[&payer_signer], + &[&payer_signer, &testing_context.testing_actors.owner.keypair()], 1000000000, 1000000000, ) @@ -200,7 +200,7 @@ pub async fn initialize_program( test_context, &[instruction], Some(&payer_signer.pubkey()), - &[&payer_signer,], + &[&payer_signer, &testing_context.testing_actors.owner.keypair()], 1000000000, 1000000000, ) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs index 3c520e392..c93101997 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/initialize_and_misc.rs @@ -131,7 +131,7 @@ pub async fn test_local_token_router_endpoint_creation() { &config, ) .await; - let custodian = initialize_state.auction_accounts().unwrap().custodian; + let custodian = initialize_state.initialized().unwrap().custodian_address; let owner = &testing_engine.testing_context.testing_actors.owner; let _local_token_router_endpoint = add_local_router_endpoint_ix( &testing_engine.testing_context, diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index fa7dee61c..0cb2d1f61 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -186,10 +186,8 @@ pub async fn test_place_initial_offer_shim_and_improve_offer_shimless() { /// Test that place initial offer and create fast market order can be done in one transaction #[tokio::test] pub async fn test_place_initial_offer_and_create_fast_market_order_in_one_transaction() { - let config = Box::new(CombinedInstructionConfig::create_fast_market_order_and_place_initial_offer()); - let vaa_args = - vec![VaaArgs { - post_vaa: false, + let vaa_args = vec![VaaArgs { + post_vaa: false, ..VaaArgs::default() }]; let (testing_context, mut test_context) = setup_environment( @@ -205,9 +203,23 @@ pub async fn test_place_initial_offer_and_create_fast_market_order_in_one_transa CreateCctpRouterEndpointsInstructionConfig::default(), ), ]; - let initial_state = testing_engine.execute(&mut test_context, initialize_instruction_triggers, None).await; - let instruction_triggers = vec![CombinationTrigger::CreateFastMarketOrderAndPlaceInitialOffer(config)]; - testing_engine.execute(&mut test_context, instruction_triggers, Some(initial_state)).await; + let initial_state = testing_engine + .execute(&mut test_context, initialize_instruction_triggers, None) + .await; + let config = Box::new( + CombinedInstructionConfig::create_fast_market_order_and_place_initial_offer( + &testing_engine.testing_context.testing_actors, + &initial_state, + &testing_engine + .testing_context + .get_matching_engine_program_id(), + ), + ); + let instruction_triggers = + vec![CombinationTrigger::CreateFastMarketOrderAndPlaceInitialOffer(config)]; + testing_engine + .execute(&mut test_context, instruction_triggers, Some(initial_state)) + .await; } /* Sad path tests section @@ -267,7 +279,7 @@ pub async fn test_place_initial_offer_shimless_blocks_shim() { InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { actor: TestingActorEnum::Solver(1), expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: 0, error_string: TransactionError::AccountInUse.to_string(), }), @@ -326,7 +338,7 @@ pub async fn test_place_initial_offer_shim_blocks_shimless() { #[tokio::test] pub async fn test_place_initial_offer_shim_fails_usdt_token_account() { let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: 3, // Token spl transfer error code when mint does not match error_string: "Invalid argument".to_string(), }; @@ -346,7 +358,7 @@ pub async fn test_place_initial_shim_offer_fails_usdt_mint_address() { ..PlaceInitialOfferCustomAccounts::default() }; let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::InvalidMint), // Token spl transfer error code when mint does not match error_string: "Invalid mint".to_string(), }; @@ -390,7 +402,7 @@ pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig { fast_market_order_address: OverwriteCurrentState::Some(fake_fast_market_order_address), expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(ErrorCode::ConstraintOwner), error_string: "Fast market order account owner is invalid".to_string(), }), @@ -421,7 +433,7 @@ pub async fn test_place_initial_offer_shim_fails_when_offer_greater_than_max_fee .create_vaa_args_and_initial_offer_config(); let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::OfferPriceTooHigh), error_string: "Offer price is greater than max fee".to_string(), }; @@ -451,7 +463,7 @@ pub async fn test_place_initial_offer_shim_fails_when_amount_in_is_u64_max() { .create_vaa_args_and_initial_offer_config(); let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::U64Overflow), error_string: "U64Overflow".to_string(), }; @@ -481,7 +493,7 @@ pub async fn test_place_initial_offer_shim_fails_when_max_fee_and_amount_in_sum_ .create_vaa_args_and_initial_offer_config(); let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::U64Overflow), error_string: "U64Overflow".to_string(), }; @@ -528,7 +540,7 @@ pub async fn test_place_initial_offer_shim_fails_when_vaa_is_expired() { let place_initial_offer_config = PlaceInitialOfferInstructionConfig { expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::FastMarketOrderExpired), error_string: "Fast market order has expired".to_string(), }), @@ -588,7 +600,7 @@ pub async fn test_place_initial_offer_shim_fails_custodian_is_paused() { let place_initial_offer_config = PlaceInitialOfferInstructionConfig { expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::Paused), error_string: "Fast market order account owner is invalid".to_string(), }), @@ -614,7 +626,7 @@ pub async fn test_place_initial_offer_shim_fails_back_to_back() { .await; let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: 0, error_string: "Already in use".to_string(), }; diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 9d1284b26..f4384e8fb 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -21,6 +21,7 @@ use std::{ }; use crate::{ + shimful::fast_market_order_shim::create_fast_market_order_state_from_vaa_data, shimless::initialize::AuctionParametersConfig, utils::{ auction::{ActiveAuctionState, AuctionAccounts}, @@ -29,6 +30,7 @@ use crate::{ }, }; use anchor_lang::prelude::*; +use matching_engine::state::FastMarketOrder as FastMarketOrderState; use solana_program_test::ProgramTestContext; use solana_sdk::signature::Keypair; @@ -292,6 +294,7 @@ pub struct PlaceInitialOfferInstructionConfig { pub test_vaa_pair_index: usize, pub offer_price: u64, pub payer_signer: Option>, + pub close_account_refund_recipient: Option, pub fast_market_order_address: OverwriteCurrentState, pub custom_accounts: OverwriteCurrentState, pub spl_token_enum: SplTokenEnum, @@ -350,6 +353,7 @@ impl Default for PlaceInitialOfferInstructionConfig { test_vaa_pair_index: 0, offer_price: 1__000_000, payer_signer: None, + close_account_refund_recipient: None, fast_market_order_address: None, custom_accounts: None, spl_token_enum: SplTokenEnum::Usdc, @@ -643,12 +647,36 @@ impl Default for CombinedInstructionConfig { } impl CombinedInstructionConfig { - pub fn create_fast_market_order_and_place_initial_offer( + pub fn create_fast_market_order_and_place_initial_offer( + testing_actors: &TestingActors, + current_state: &TestingEngineState, + program_id: &Pubkey, ) -> Self { + let test_vaa_pair = current_state.get_test_vaa_pair(0); + let fast_transfer_vaa = test_vaa_pair.fast_transfer_vaa.clone(); + let fast_market_order = create_fast_market_order_state_from_vaa_data( + &fast_transfer_vaa.vaa_data, + testing_actors.solvers[0].pubkey(), + ); + let (fast_market_order_address, _fast_market_order_bump) = Pubkey::find_program_address( + &[ + FastMarketOrderState::SEED_PREFIX, + &fast_market_order.digest(), + &fast_market_order.close_account_refund_recipient.as_ref(), + ], + program_id, + ); + Self { - create_fast_market_order_config: Some(InitializeFastMarketOrderShimInstructionConfig::default()), - place_initial_offer_config: Some(PlaceInitialOfferInstructionConfig::default()), + create_fast_market_order_config: Some( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + place_initial_offer_config: Some(PlaceInitialOfferInstructionConfig { + close_account_refund_recipient: Some(testing_actors.solvers[0].actor.pubkey()), + fast_market_order_address: Some(fast_market_order_address), + ..PlaceInitialOfferInstructionConfig::default() + }), ..Default::default() } } -} \ No newline at end of file +} diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index c141742c5..de112cb6b 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -944,7 +944,6 @@ impl TestingEngine { .payer_signer .clone() .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - println!("Got here 0"); let guardian_signature_info = create_guardian_signatures( &self.testing_context, test_context, @@ -981,7 +980,6 @@ impl TestingEngine { .payer_signer .clone() .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - println!("Got here 1"); let transaction = self .testing_context .create_transaction( @@ -998,7 +996,6 @@ impl TestingEngine { 1000000000, ) .await; - println!("Got here 2"); let actor_usdc_balance_before = place_initial_offer_config .actor .get_actor(&self.testing_context.testing_actors) @@ -1012,7 +1009,6 @@ impl TestingEngine { self.testing_context .execute_and_verify_transaction(test_context, transaction, None) .await; - println!("Got here 3"); let fast_market_order_created_state = TestingEngineState::FastMarketOrderAccountCreated { base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), From 9a52c0687f13f266b6f086f56ab81730b29d3157 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 8 May 2025 19:33:28 +0100 Subject: [PATCH 088/112] docstrings edited and added --- .../tests/shimful/fast_market_order_shim.rs | 19 +- .../tests/shimful/shims_execute_order.rs | 11 ++ .../tests/shimful/shims_make_offer.rs | 28 +++ .../shimful/shims_prepare_order_response.rs | 60 ++++++- .../tests/testing_engine/engine.rs | 18 ++ .../tests/testing_engine/setup.rs | 166 +++++++++++++++++- 6 files changed, 281 insertions(+), 21 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index faed01419..e40513f1c 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -33,16 +33,14 @@ use wormhole_io::TypePrefixedPayload; /// # Arguments /// /// * `testing_context` - The testing context -/// * `payer_signer` - The payer signer keypair -/// * `fast_market_order` - The fast market order state -/// * `guardian_set_pubkey` - The guardian set pubkey -/// * `guardian_signatures_pubkey` - The guardian signatures pubkey -/// * `guardian_set_bump` - The guardian set bump +/// * `test_context` - The program test context /// * `expected_error` - The expected error +/// * `current_state` - The current testing engine state +/// * `config` - The initialization configuration /// -/// # Asserts +/// # Returns /// -/// * The expected error, if any, is reached when executing the instruction +/// * `TestingEngineState` - The updated testing engine state pub async fn initialize_fast_market_order_shimful( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -134,9 +132,7 @@ pub async fn initialize_fast_market_order_shimful( /// * `payer_signer` - The payer signer keypair /// * `program_id` - The program id /// * `fast_market_order` - The fast market order state -/// * `guardian_set_pubkey` - The guardian set pubkey -/// * `guardian_signatures_pubkey` - The guardian signatures pubkey -/// * `guardian_set_bump` - The guardian set bump +/// * `guardian_signature_info` - Information about guardian signatures /// /// # Returns /// @@ -184,7 +180,8 @@ pub fn initialize_fast_market_order_shimful_instruction( /// # Arguments /// /// * `testing_context` - The testing context -/// * `refund_recipient_keypair` - The refund recipient keypair that will receive the refund after closing the fast market order account +/// * `test_context` - The program test context +/// * `refund_recipient_keypair` - The refund recipient keypair that will receive the refund /// * `fast_market_order_address` - The fast market order account address /// * `expected_error` - The expected error /// diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index a62878edb..b999c3343 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -215,6 +215,17 @@ pub fn create_execute_order_fallback_fixture( } } +/// Create the execute order shim accounts +/// +/// # Arguments +/// +/// * `execute_order_fallback_accounts` - The execute order fallback accounts +/// * `execute_order_fallback_fixture` - The execute order fallback fixture +/// * `clock_id` - The clock id +/// +/// # Returns +/// +/// The execute order shim accounts pub fn create_execute_order_shim_accounts<'ix>( execute_order_fallback_accounts: &'ix ExecuteOrderFallbackAccounts, execute_order_fallback_fixture: &'ix ExecuteOrderFallbackFixture, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 0f12af2a2..397c21902 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -90,6 +90,20 @@ pub async fn place_initial_offer_shimful( .await } +/// Evaluate the place initial offer shimful state +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `current_state` - The current state +/// * `config` - The config +/// * `actor_usdc_balance_before` - The actor USDC balance before +/// * `place_initial_offer_accounts` - The place initial offer shimful accounts +/// +/// # Returns +/// +/// The testing engine state after the place initial offer shimful instruction pub async fn evaluate_place_initial_offer_shimful_state( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -170,6 +184,20 @@ pub async fn evaluate_place_initial_offer_shimful_state( current_state.clone() } +/// Place the initial offer shimful instruction +/// +/// Creates the place initial offer shimful instruction +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `current_state` - The current state +/// * `config` - The config +/// +/// # Returns +/// +/// The place initial offer shimful instruction pub async fn place_initial_offer_shimful_instruction( testing_context: &TestingContext, test_context: &mut ProgramTestContext, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index a7814738d..bb9548774 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -145,15 +145,38 @@ impl PrepareOrderResponseShimAccountsFixture { } } -pub struct PrepareOrderResponseShimDataFixture { +/// Prepare order response shim data helper +/// +/// This struct is a helper struct used to create the data for the prepare order response instruction +/// +/// # Fields +/// +/// * `encoded_cctp_message` - The encoded CCTP message +/// * `cctp_attestation` - The CCTP attestation +/// * `finalized_vaa_message_args` - The finalized VAA message args +/// * `fast_market_order` - The fast market order +pub struct PrepareOrderResponseShimDataHelper { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, pub finalized_vaa_message_args: FinalizedVaaMessageArgs, pub fast_market_order: FastMarketOrderState, } -// Helper struct for creating the data for the prepare order response instruction -impl PrepareOrderResponseShimDataFixture { +impl PrepareOrderResponseShimDataHelper { + /// Create a new prepare order response shim data helper + /// + /// # Arguments + /// + /// * `encoded_cctp_message` - The encoded CCTP message + /// * `cctp_attestation` - The CCTP attestation + /// * `consistency_level` - The consistency level + /// * `base_fee` - The base fee + /// * `fast_market_order` - The fast market order + /// * `guardian_set_bump` - The guardian set bump + /// + /// # Returns + /// + /// The prepare order response shim data helper pub fn new( encoded_cctp_message: Vec, cctp_attestation: Vec, @@ -183,12 +206,25 @@ impl PrepareOrderResponseShimDataFixture { } /// Executes the instruction that prepares the order response for the CCTP shim +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `payer_signer` - The payer signer +/// * `accounts` - The prepare order response shim accounts +/// * `data` - The prepare order response shim data +/// * `expected_error` - The expected error +/// +/// # Returns +/// +/// The prepare order response shim fixture pub async fn prepare_order_response_cctp_shim( testing_context: &TestingContext, test_context: &mut ProgramTestContext, payer_signer: &Rc, accounts: PrepareOrderResponseShimAccountsFixture, - data: PrepareOrderResponseShimDataFixture, + data: PrepareOrderResponseShimDataHelper, expected_error: Option<&ExpectedError>, ) -> Option { let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); @@ -280,6 +316,20 @@ pub async fn prepare_order_response_cctp_shim( } } +/// Prepare order response test +/// +/// Executes the prepare order response instruction in a testing context +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `config` - The prepare order response instruction config +/// * `current_state` - The current state +/// +/// # Returns +/// +/// The prepare order response shim fixture (none if failed) pub async fn prepare_order_response_test( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -337,7 +387,7 @@ pub async fn prepare_order_response_test( .unwrap(); let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); - let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataFixture::new( + let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataHelper::new( cctp_token_burn_message.encoded_cctp_burn_message, cctp_token_burn_message.cctp_attestation, deposit_vaa_data.consistency_level, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index de112cb6b..9f90b3bea 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -391,6 +391,10 @@ impl TestingEngine { } } + // -------------------------------------------------------------------------------------------- + // Instruction trigger functions + // -------------------------------------------------------------------------------------------- + /// Creates the initial state for the testing engine pub fn create_initial_state(&self) -> TestingEngineState { let fixture_accounts = self @@ -536,6 +540,8 @@ impl TestingEngine { order_executed: current_state.order_executed().cloned(), } } + + /// Instruction trigger function for placing an initial offer async fn place_initial_offer_shimless( &self, test_context: &mut ProgramTestContext, @@ -860,6 +866,10 @@ impl TestingEngine { } } + // -------------------------------------------------------------------------------------------- + // Verification trigger functions + // -------------------------------------------------------------------------------------------- + async fn verify_auction_state( &self, test_context: &mut ProgramTestContext, @@ -923,6 +933,10 @@ impl TestingEngine { current_state.clone() } + // -------------------------------------------------------------------------------------------- + // Combination trigger functions + // -------------------------------------------------------------------------------------------- + async fn create_fast_market_order_and_place_initial_offer( &self, test_context: &mut ProgramTestContext, @@ -1038,6 +1052,10 @@ impl TestingEngine { .await } + // -------------------------------------------------------------------------------------------- + // Helper functions for manipulating the state + // -------------------------------------------------------------------------------------------- + pub async fn make_auction_passed_penalty_period( &self, test_context: &mut ProgramTestContext, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 6407bbbdb..997e5b3d0 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -378,6 +378,20 @@ impl TestingContext { Ok(()) } + /// Creates a transaction + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `instructions` - The instructions to include in the transaction + /// * `payer` - The payer of the transaction + /// * `signers` - The signers of the transaction + /// * `compute_unit_price` - The compute unit price of the transaction + /// * `compute_unit_limit` - The compute unit limit of the transaction + /// + /// # Returns + /// + /// The transaction pub async fn create_transaction( &self, test_context: &mut ProgramTestContext, @@ -399,7 +413,13 @@ impl TestingContext { Transaction::new_signed_with_payer(&all_instructions, payer, signers, last_blockhash) } - // TODO: Edit to handle multiple instructions in a single transaction + /// Executes a transaction and verifies that the transaction either succeeds or fails as expected + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `transaction` - The transaction to execute + /// * `expected_error` - The expected error pub async fn execute_and_verify_transaction( &self, test_context: &mut ProgramTestContext, @@ -457,10 +477,27 @@ impl TestingContext { } /// Gets the balances of all the test actors + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + /// The balances of all the test actors pub async fn get_balances(&self, test_context: &mut ProgramTestContext) -> Balances { Balances::new(&self.testing_actors, test_context).await } + /// Gets the current timestamp + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + /// The current timestamp as an i64 pub async fn get_current_timestamp(&self, test_context: &mut ProgramTestContext) -> i64 { let clock = test_context .banks_client @@ -470,6 +507,12 @@ impl TestingContext { clock.unix_timestamp } + /// Fast forwards the clock to the given timestamp + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `target_timestamp` - The timestamp to fast forward to pub async fn fast_forward_to_timestamp( &self, test_context: &mut ProgramTestContext, @@ -484,6 +527,12 @@ impl TestingContext { assert!(current_timestamp >= target_timestamp); } + /// Makes the fast transfer VAA expired + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `seconds_after_expiry` - The number of seconds after the VAA expiration time to make the VAA expired pub async fn make_fast_transfer_vaa_expired( &self, test_context: &mut ProgramTestContext, @@ -499,6 +548,15 @@ impl TestingContext { .await; } + /// Gets the remote token messenger + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + /// The remote token messenger as a CctpRemoteTokenMessenger pub async fn get_remote_token_messenger( &self, test_context: &mut ProgramTestContext, @@ -549,14 +607,29 @@ impl Solver { } } + /// Gets the keypair + /// + /// # Returns + /// + /// The keypair as an Rc pub fn keypair(&self) -> Rc { self.actor.keypair.clone() } + /// Gets the pubkey + /// + /// # Returns + /// + /// The pubkey as a Pubkey pub fn pubkey(&self) -> Pubkey { self.actor.keypair.pubkey() } + /// Gets the token account address + /// + /// # Returns + /// + /// The token account address as an Option pub fn token_account_address(&self) -> Option { self.actor.usdc_token_account.as_ref().map(|t| t.address) } @@ -580,6 +653,16 @@ impl Solver { .await; } + /// Gets the balance of the token account + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `spl_token_enum` - The SPL token enum + /// + /// # Returns + /// + /// The balance of the token account as a u64 pub async fn get_token_account_balance( &self, test_context: &mut ProgramTestContext, @@ -590,6 +673,15 @@ impl Solver { .await } + /// Gets the balance of the actor's lamports + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + /// The balance of the actor's lamports as a u64 pub async fn get_lamport_balance(&self, test_context: &mut ProgramTestContext) -> u64 { self.actor.get_lamport_balance(test_context).await } @@ -631,13 +723,34 @@ impl TestingActor { usdt_token_account, } } + + /// Gets the pubkey + /// + /// # Returns + /// + /// The pubkey as a Pubkey pub fn pubkey(&self) -> Pubkey { self.keypair.pubkey() } + + /// Gets the keypair + /// + /// # Returns + /// + /// The keypair as an Rc pub fn keypair(&self) -> Rc { self.keypair.clone() } + /// Gets the token account address + /// + /// # Arguments + /// + /// * `spl_token_enum` - The SPL token enum + /// + /// # Returns + /// + /// The token account address if it exists, otherwise None pub fn token_account_address(&self, spl_token_enum: &SplTokenEnum) -> Option { match spl_token_enum { SplTokenEnum::Usdc => self.usdc_token_account.as_ref().map(|t| t.address), @@ -650,6 +763,11 @@ impl TestingActor { /// # Arguments /// /// * `test_context` - The test context + /// * `spl_token_enum` - The SPL token enum + /// + /// # Returns + /// + /// The balance of the token account as a u64 pub async fn get_token_account_balance( &self, test_context: &mut ProgramTestContext, @@ -672,6 +790,15 @@ impl TestingActor { } } + /// Gets the balance of the actor's lamports + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// + /// # Returns + /// + /// The balance of the actor's lamports as a u64 pub async fn get_lamport_balance(&self, test_context: &mut ProgramTestContext) -> u64 { test_context .banks_client @@ -721,6 +848,12 @@ impl TestingActor { .expect("Failed to approve USDC"); } + /// Closes a token account + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `spl_token_enum` - The SPL token enum pub async fn close_token_account( &self, test_context: &mut ProgramTestContext, @@ -937,7 +1070,11 @@ impl TestingActors { actors } - /// Transfer Lamports to Executors + /// Transfers 10000000000 Lamports to all the actors + /// + /// # Arguments + /// + /// * `test_context` - The test context async fn airdrop_all(&self, test_context: &mut ProgramTestContext) { airdrop(test_context, &self.payer_signer.pubkey(), 10000000000).await; airdrop(test_context, &self.owner.pubkey(), 10000000000).await; @@ -950,7 +1087,14 @@ impl TestingActors { airdrop(test_context, &self.liquidator.pubkey(), 10000000000).await; } - /// Set up ATAs for Various Owners + /// Creates USDC associated token accounts + /// + /// Creates usdc associated token accounts for all actors that expect to have them + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `usdc_mint_address` - The USDC mint address async fn create_usdc_atas( &mut self, test_context: &mut ProgramTestContext, @@ -970,7 +1114,14 @@ impl TestingActors { } } - /// Create usdt associated token accounts + /// Creates USDT associated token accounts + /// + /// Creates usdt associated token accounts for all actors that expect to have them + /// + /// # Arguments + /// + /// * `test_context` - The test context + /// * `usdt_mint_address` - The USDT mint address pub async fn create_usdt_atas( &mut self, test_context: &mut ProgramTestContext, @@ -990,6 +1141,11 @@ impl TestingActors { } } + /// Gets an actor + /// + /// # Arguments + /// + /// * `actor_enum` - The actor enum pub fn get_actor(&self, actor_enum: &TestingActorEnum) -> &TestingActor { match actor_enum { TestingActorEnum::Owner => &self.owner, @@ -1001,7 +1157,7 @@ impl TestingActors { } } - /// Add solvers to the testing actors + /// Add solvers to the testing actors struct #[allow(dead_code)] pub async fn add_solvers( &mut self, From 3f2a8e2fa13e06d05bfb32ec9e6630e70ce36c54 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 9 May 2025 15:00:29 +0100 Subject: [PATCH 089/112] refactored and docstringed matching-engine-testing/tests/shimful directory --- .../tests/shimful/fast_market_order_shim.rs | 4 +- .../tests/shimful/post_message.rs | 15 + .../tests/shimful/shims_execute_order.rs | 380 ++++++------ .../tests/shimful/shims_make_offer.rs | 19 +- .../shimful/shims_prepare_order_response.rs | 579 +++++++++--------- .../tests/shimful/verify_shim.rs | 7 + .../tests/shimless/initialize.rs | 18 +- .../tests/test_scenarios/execute_order.rs | 6 +- .../tests/testing_engine/config.rs | 2 +- .../tests/testing_engine/engine.rs | 74 +-- .../tests/testing_engine/setup.rs | 12 +- .../tests/utils/auction.rs | 2 +- 12 files changed, 545 insertions(+), 573 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index e40513f1c..849539942 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -92,8 +92,8 @@ pub async fn initialize_fast_market_order_shimful( &[initialize_fast_market_order_ix], Some(&payer_signer.pubkey()), &[&payer_signer], - 1000000000, - 1000000000, + None, + None, ) .await; testing_context diff --git a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs index f7747696f..a80109339 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/post_message.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/post_message.rs @@ -110,6 +110,21 @@ pub async fn set_up_post_message_transaction_test( ); } +/// Set up post message transaction +/// +/// This function sets up a post message transaction +/// +/// # Arguments +/// +/// * `payload` - The payload to post +/// * `payer_signer` - The payer signer +/// * `emitter_signer` - The emitter signer +/// * `recent_blockhash` - The recent blockhash +/// +/// # Returns +/// +/// * `VersionedTransaction` - The versioned transaction that can be executed to post the message +/// * `BumpCosts` - The bump costs for the message and sequence fn set_up_post_message_transaction( payload: &[u8], payer_signer: &Keypair, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index b999c3343..02c1159b7 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -1,8 +1,6 @@ -use crate::testing_engine::config::{ - ExecuteOrderInstructionConfig, ExpectedError, InstructionConfig, -}; +use crate::testing_engine::config::{ExecuteOrderInstructionConfig, InstructionConfig}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; -use crate::testing_engine::state::TestingEngineState; +use crate::testing_engine::state::{OrderExecutedState, TestingEngineState}; use super::super::utils; use anchor_spl::token::spl_token; @@ -11,7 +9,7 @@ use common::wormhole_cctp_solana::cctp::{ }; use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; use solana_program_test::ProgramTestContext; -use solana_sdk::{pubkey::Pubkey, signer::Signer, sysvar::SysvarId, transaction::Transaction}; +use solana_sdk::{pubkey::Pubkey, signer::Signer, sysvar::SysvarId}; use utils::constants::*; use wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID; use wormhole_svm_definitions::{ @@ -22,7 +20,93 @@ use wormhole_svm_definitions::{ EVENT_AUTHORITY_SEED, }; -pub struct ExecuteOrderFallbackAccounts { +/// Execute an order using the shim +/// +/// # Arguments +/// +/// * `testing_context` - The testing context of the testing engine +/// * `test_context` - Mutable reference to the test context +/// * `current_state` - The current state of the testing engine +/// * `config` - The execute order instruction config +/// +/// # Returns +/// +/// The new state of the testing engine +pub async fn execute_order_shimful( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, +) -> TestingEngineState { + let expected_error = config.expected_error(); + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Pre-made fixture accounts not found"); + + let execute_order_fallback_accounts = ExecuteOrderShimfulAccounts::new( + testing_context, + current_state, + config, + &fixture_accounts, + config.fast_market_order_address, + ); + let program_id = &testing_context.get_matching_engine_program_id(); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + + let clock_id = solana_program::clock::Clock::id(); + let execute_order_ix_accounts = + create_execute_order_shim_accounts(&execute_order_fallback_accounts, &clock_id); + + let execute_order_ix = ExecuteOrderCctpShim { + program_id, + accounts: execute_order_ix_accounts, + } + .instruction(); + + let slots_to_fast_forward = config.fast_forward_slots; + if slots_to_fast_forward > 0 { + crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) + .await; + } + let transaction = testing_context + .create_transaction( + test_context, + &[execute_order_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + None, + None, + ) + .await; + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + if config.expected_error.is_none() { + let auction_accounts = current_state + .auction_accounts() + .expect("Auction accounts not found"); + let order_executed_state = + create_order_executed_state(config, &execute_order_fallback_accounts); + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + order_prepared: current_state.order_prepared().cloned(), + } + } else { + current_state.clone() + } +} + +/// A helper struct for the accounts for the execute order shimful instruction that disregards the lifetime +struct ExecuteOrderShimfulAccounts { pub signer: Pubkey, pub custodian: Pubkey, pub fast_market_order_address: Pubkey, @@ -35,18 +119,35 @@ pub struct ExecuteOrderFallbackAccounts { pub to_router_endpoint: Pubkey, pub remote_token_messenger: Pubkey, pub token_messenger: Pubkey, + pub local_token: Pubkey, + pub token_messenger_minter_sender_authority: Pubkey, + pub token_messenger_minter_event_authority: Pubkey, + pub messenger_transmitter_config: Pubkey, + pub token_minter: Pubkey, + pub executor_token: Pubkey, + pub cctp_message: Pubkey, + pub post_message_sequence: Pubkey, + pub post_message_message: Pubkey, } -impl ExecuteOrderFallbackAccounts { +impl ExecuteOrderShimfulAccounts { pub fn new( + testing_context: &TestingContext, current_state: &TestingEngineState, - payer_signer: &Pubkey, + config: &ExecuteOrderInstructionConfig, fixture_accounts: &utils::account_fixtures::FixtureAccounts, override_fast_market_order_address: Option, ) -> Self { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let transfer_direction = current_state.base().transfer_direction; let auction_accounts = current_state.auction_accounts().unwrap(); let active_auction_state = current_state.auction_state().get_active_auction().unwrap(); + let initial_participant = active_auction_state.initial_offer.participant; + let active_auction = active_auction_state.auction_address; + let custodian = auction_accounts.custodian; let fast_market_order_address = override_fast_market_order_address.unwrap_or_else(|| { current_state .fast_market_order() @@ -62,9 +163,53 @@ impl ExecuteOrderFallbackAccounts { } _ => panic!("Unsupported transfer direction"), }; + let program_id = &testing_context.get_matching_engine_program_id(); + let cctp_message = Pubkey::find_program_address( + &[common::CCTP_MESSAGE_SEED_PREFIX, &active_auction.to_bytes()], + program_id, + ) + .0; + let token_messenger_minter_sender_authority = Pubkey::find_program_address( + &[b"sender_authority"], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let messenger_transmitter_config = Pubkey::find_program_address( + &[b"message_transmitter"], + &MESSAGE_TRANSMITTER_PROGRAM_ID, + ) + .0; + let token_messenger = + Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID) + .0; + assert_eq!(token_messenger, fixture_accounts.token_messenger); + let token_minter = + Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let local_token = Pubkey::find_program_address( + &[b"local_token", &USDC_MINT.to_bytes()], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let token_messenger_minter_event_authority = Pubkey::find_program_address( + &[EVENT_AUTHORITY_SEED], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address( + &custodian, + &CORE_BRIDGE_PROGRAM_ID, + ) + .0; + let post_message_message = wormhole_svm_definitions::find_shim_message_address( + &custodian, + &POST_MESSAGE_SHIM_PROGRAM_ID, + ) + .0; + let solver = config.actor_enum.get_actor(&testing_context.testing_actors); + let executor_token = solver.token_account_address(&config.token_enum).unwrap(); Self { - signer: *payer_signer, + signer: payer_signer.pubkey(), custodian: auction_accounts.custodian, fast_market_order_address, active_auction: active_auction_state.auction_address, @@ -72,146 +217,32 @@ impl ExecuteOrderFallbackAccounts { active_auction_config: auction_accounts.auction_config, active_auction_best_offer_token: auction_accounts.offer_token, initial_offer_token: auction_accounts.offer_token, - initial_participant: *payer_signer, + initial_participant, to_router_endpoint: auction_accounts.to_router_endpoint, remote_token_messenger, - token_messenger: fixture_accounts.token_messenger, - } - } -} - -pub struct ExecuteOrderFallbackFixture { - pub cctp_message: Pubkey, - pub post_message_sequence: Pubkey, - pub post_message_message: Pubkey, - pub accounts: ExecuteOrderFallbackFixtureAccounts, -} - -pub struct ExecuteOrderFallbackFixtureAccounts { - pub local_token: Pubkey, - pub token_messenger: Pubkey, - pub remote_token_messenger: Pubkey, - pub token_messenger_minter_sender_authority: Pubkey, - pub token_messenger_minter_event_authority: Pubkey, - pub messenger_transmitter_config: Pubkey, - pub token_minter: Pubkey, - pub executor_token: Pubkey, -} - -pub async fn execute_order_shimful( - testing_context: &TestingContext, - test_context: &mut ProgramTestContext, - config: &ExecuteOrderInstructionConfig, - execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, - expected_error: Option<&ExpectedError>, -) -> Option { - let program_id = &testing_context.get_matching_engine_program_id(); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - - let execute_order_fallback_fixture = create_execute_order_fallback_fixture( - testing_context, - config, - execute_order_fallback_accounts, - ); - let clock_id = solana_program::clock::Clock::id(); - let execute_order_ix_accounts = create_execute_order_shim_accounts( - execute_order_fallback_accounts, - &execute_order_fallback_fixture, - &clock_id, - ); - - let execute_order_ix = ExecuteOrderCctpShim { - program_id, - accounts: execute_order_ix_accounts, - } - .instruction(); - - // Considering fast forwarding blocks here for deadline to be reached - let recent_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .unwrap(); - let slots_to_fast_forward = config.fast_forward_slots; - if slots_to_fast_forward > 0 { - crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) - .await; - } - let transaction = Transaction::new_signed_with_payer( - &[execute_order_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - recent_blockhash, - ); - testing_context - .execute_and_verify_transaction(test_context, transaction, expected_error) - .await; - if expected_error.is_none() { - Some(execute_order_fallback_fixture) - } else { - None - } -} - -pub fn create_execute_order_fallback_fixture( - testing_context: &TestingContext, - config: &ExecuteOrderInstructionConfig, - execute_order_fallback_accounts: &ExecuteOrderFallbackAccounts, -) -> ExecuteOrderFallbackFixture { - let program_id = &testing_context.get_matching_engine_program_id(); - let cctp_message = Pubkey::find_program_address( - &[ - common::CCTP_MESSAGE_SEED_PREFIX, - &execute_order_fallback_accounts.active_auction.to_bytes(), - ], - program_id, - ) - .0; - let token_messenger_minter_sender_authority = - Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let messenger_transmitter_config = - Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; - let token_messenger = - Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let remote_token_messenger = execute_order_fallback_accounts.remote_token_messenger; - let token_minter = - Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; - let local_token = Pubkey::find_program_address( - &[b"local_token", &USDC_MINT.to_bytes()], - &TOKEN_MESSENGER_MINTER_PROGRAM_ID, - ) - .0; - let token_messenger_minter_event_authority = - &Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID) - .0; - let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address( - &execute_order_fallback_accounts.custodian, - &CORE_BRIDGE_PROGRAM_ID, - ) - .0; - let post_message_message = wormhole_svm_definitions::find_shim_message_address( - &execute_order_fallback_accounts.custodian, - &POST_MESSAGE_SHIM_PROGRAM_ID, - ) - .0; - let solver = config.actor_enum.get_actor(&testing_context.testing_actors); - let executor_token = solver.token_account_address(&config.token_enum).unwrap(); - ExecuteOrderFallbackFixture { - cctp_message, - post_message_sequence, - post_message_message, - accounts: ExecuteOrderFallbackFixtureAccounts { - local_token, token_messenger, - remote_token_messenger, + local_token, token_messenger_minter_sender_authority, - token_messenger_minter_event_authority: *token_messenger_minter_event_authority, + token_messenger_minter_event_authority, messenger_transmitter_config, token_minter, executor_token, - }, + cctp_message, + post_message_sequence, + post_message_message, + } + } +} + +fn create_order_executed_state( + config: &ExecuteOrderInstructionConfig, + execute_order_fallback_accounts: &ExecuteOrderShimfulAccounts, +) -> OrderExecutedState { + OrderExecutedState { + cctp_message: execute_order_fallback_accounts.cctp_message, + post_message_sequence: Some(execute_order_fallback_accounts.post_message_sequence), + post_message_message: Some(execute_order_fallback_accounts.post_message_message), + actor_enum: config.actor_enum, } } @@ -226,14 +257,13 @@ pub fn create_execute_order_fallback_fixture( /// # Returns /// /// The execute order shim accounts -pub fn create_execute_order_shim_accounts<'ix>( - execute_order_fallback_accounts: &'ix ExecuteOrderFallbackAccounts, - execute_order_fallback_fixture: &'ix ExecuteOrderFallbackFixture, +fn create_execute_order_shim_accounts<'ix>( + execute_order_fallback_accounts: &'ix ExecuteOrderShimfulAccounts, clock_id: &'ix Pubkey, ) -> ExecuteOrderShimAccounts<'ix> { ExecuteOrderShimAccounts { signer: &execute_order_fallback_accounts.signer, // 0 - cctp_message: &execute_order_fallback_fixture.cctp_message, // 1 + cctp_message: &execute_order_fallback_accounts.cctp_message, // 1 custodian: &execute_order_fallback_accounts.custodian, // 2 fast_market_order: &execute_order_fallback_accounts.fast_market_order_address, // 3 active_auction: &execute_order_fallback_accounts.active_auction, // 4 @@ -241,33 +271,25 @@ pub fn create_execute_order_shim_accounts<'ix>( active_auction_config: &execute_order_fallback_accounts.active_auction_config, // 6 active_auction_best_offer_token: &execute_order_fallback_accounts .active_auction_best_offer_token, // 7 - executor_token: &execute_order_fallback_fixture.accounts.executor_token, // 8 + executor_token: &execute_order_fallback_accounts.executor_token, // 8 initial_offer_token: &execute_order_fallback_accounts.initial_offer_token, // 9 initial_participant: &execute_order_fallback_accounts.initial_participant, // 10 to_router_endpoint: &execute_order_fallback_accounts.to_router_endpoint, // 11 post_message_shim_program: &POST_MESSAGE_SHIM_PROGRAM_ID, // 12 - core_bridge_emitter_sequence: &execute_order_fallback_fixture.post_message_sequence, // 13 - post_shim_message: &execute_order_fallback_fixture.post_message_message, // 14 + core_bridge_emitter_sequence: &execute_order_fallback_accounts.post_message_sequence, // 13 + post_shim_message: &execute_order_fallback_accounts.post_message_message, // 14 cctp_deposit_for_burn_mint: &USDC_MINT, // 15 cctp_deposit_for_burn_token_messenger_minter_sender_authority: - &execute_order_fallback_fixture - .accounts - .token_messenger_minter_sender_authority, // 16 - cctp_deposit_for_burn_message_transmitter_config: &execute_order_fallback_fixture - .accounts + &execute_order_fallback_accounts.token_messenger_minter_sender_authority, // 16 + cctp_deposit_for_burn_message_transmitter_config: &execute_order_fallback_accounts .messenger_transmitter_config, // 17 - cctp_deposit_for_burn_token_messenger: &execute_order_fallback_fixture - .accounts - .token_messenger, // 18 - cctp_deposit_for_burn_remote_token_messenger: &execute_order_fallback_fixture - .accounts + cctp_deposit_for_burn_token_messenger: &execute_order_fallback_accounts.token_messenger, // 18 + cctp_deposit_for_burn_remote_token_messenger: &execute_order_fallback_accounts .remote_token_messenger, // 19 - cctp_deposit_for_burn_token_minter: &execute_order_fallback_fixture.accounts.token_minter, // 20 - cctp_deposit_for_burn_local_token: &execute_order_fallback_fixture.accounts.local_token, // 21 + cctp_deposit_for_burn_token_minter: &execute_order_fallback_accounts.token_minter, // 20 + cctp_deposit_for_burn_local_token: &execute_order_fallback_accounts.local_token, // 21 cctp_deposit_for_burn_token_messenger_minter_event_authority: - &execute_order_fallback_fixture - .accounts - .token_messenger_minter_event_authority, // 22 + &execute_order_fallback_accounts.token_messenger_minter_event_authority, // 22 cctp_deposit_for_burn_token_messenger_minter_program: &TOKEN_MESSENGER_MINTER_PROGRAM_ID, // 23 cctp_deposit_for_burn_message_transmitter_program: &MESSAGE_TRANSMITTER_PROGRAM_ID, // 24 core_bridge_program: &CORE_BRIDGE_PROGRAM_ID, // 25 @@ -279,33 +301,3 @@ pub fn create_execute_order_shim_accounts<'ix>( clock: clock_id, // 31 } } - -pub async fn execute_order_shimful_test( - testing_context: &TestingContext, - test_context: &mut ProgramTestContext, - current_state: &TestingEngineState, - config: &ExecuteOrderInstructionConfig, -) -> Option { - let expected_error = config.expected_error(); - let fixture_accounts = testing_context - .get_fixture_accounts() - .expect("Pre-made fixture accounts not found"); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let execute_order_fallback_accounts = ExecuteOrderFallbackAccounts::new( - current_state, - &payer_signer.pubkey(), - &fixture_accounts, - config.fast_market_order_address, - ); - execute_order_shimful( - testing_context, - test_context, - config, - &execute_order_fallback_accounts, - expected_error, - ) - .await -} diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 397c21902..51a525b74 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -21,18 +21,15 @@ use solana_sdk::{pubkey::Pubkey, signer::Signer}; /// /// # Arguments /// -/// * `testing_context` - The testing context -/// * `payer_signer` - The payer signer -/// * `vaa_data` - The vaa data (not posted) -/// * `solver` - The solver actor that will place the initial offer -/// * `fast_market_order_account` - The fast market order account pubkey created by the create fast market order shim instruction -/// * `auction_accounts` - The auction accounts (see utils/auction.rs) -/// * `offer_price` - The offer price in the units of the offer token -/// * `expected_error` - The expected error (None if no error is expected) +/// * `testing_context` - The testing context of the testing engine +/// * `test_context` - Mutable reference to the test context +/// * `current_state` - The current state of the testing engine +/// * `config` - The config of the place initial offer instruction +/// * `expected_error` - The expected error of the place initial offer instruction /// /// # Returns /// -/// * `Option` - An auction state with the initial offer placed. None if an error is expected. +/// * `TestingEngineState` - The state of the testing engine after the place initial offer instruction /// /// # Asserts /// @@ -72,8 +69,8 @@ pub async fn place_initial_offer_shimful( &[place_initial_offer_ix], Some(&payer_signer.pubkey()), &[&payer_signer], - 1000000000, - 1000000000, + None, + None, ) .await; testing_context diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index bb9548774..6a8cfac8f 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -1,8 +1,6 @@ -use crate::testing_engine::config::{ - ExpectedError, InstructionConfig, PrepareOrderResponseInstructionConfig, -}; +use crate::testing_engine::config::{InstructionConfig, PrepareOrderResponseInstructionConfig}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; -use crate::testing_engine::state::TestingEngineState; +use crate::testing_engine::state::{OrderPreparedState, TestingEngineState}; use super::super::utils; use super::verify_shim::GuardianSignatureInfo; @@ -19,14 +17,263 @@ use matching_engine::fallback::prepare_order_response::{ use matching_engine::state::{FastMarketOrder as FastMarketOrderState, PreparedOrderResponse}; use matching_engine::CCTP_MINT_RECIPIENT; use solana_program_test::ProgramTestContext; -use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::transaction::Transaction; -use std::rc::Rc; use utils::cctp_message::{CctpMessageDecoded, UsedNonces}; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; -pub struct PrepareOrderResponseShimAccountsFixture { +/// Prepare order response cctp shimful +/// +/// Executes the prepare order response instruction in a testing context +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `config` - The prepare order response instruction config +/// * `current_state` - The current state +/// +/// # Returns +/// +/// The prepare order response shim fixture (none if failed) +pub async fn prepare_order_response_cctp_shimful( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + config: &PrepareOrderResponseInstructionConfig, + current_state: &TestingEngineState, +) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let data = PrepareOrderResponseShimDataHelper::new( + testing_context, + test_context, + current_state, + config, + ) + .await; + let cctp_message_decoded = data.decode_cctp_message(); + let accounts = PrepareOrderResponseShimAccountsHelper::new( + testing_context, + config, + current_state, + &cctp_message_decoded, + &data, + ); + + let ix_accounts = PrepareOrderResponseCctpShimAccounts { + signer: &accounts.signer, + custodian: &accounts.custodian, + fast_market_order: &accounts.fast_market_order, + from_endpoint: &accounts.from_endpoint, + to_endpoint: &accounts.to_endpoint, + prepared_order_response: &accounts.prepared_order_response, + prepared_custody_token: &accounts.prepared_custody_token, + base_fee_token: &accounts.base_fee_token, + usdc: &accounts.usdc, + cctp_mint_recipient: &accounts.cctp_mint_recipient, + cctp_message_transmitter_authority: &accounts.cctp_message_transmitter_authority, + cctp_message_transmitter_config: &accounts.cctp_message_transmitter_config, + cctp_used_nonces: &accounts.cctp_used_nonces, + cctp_message_transmitter_event_authority: &accounts + .cctp_message_transmitter_event_authority, + cctp_token_messenger: &accounts.cctp_token_messenger, + cctp_remote_token_messenger: &accounts.cctp_remote_token_messenger, + cctp_token_minter: &accounts.cctp_token_minter, + cctp_local_token: &accounts.cctp_local_token, + cctp_token_pair: &accounts.cctp_token_pair, + cctp_token_messenger_minter_event_authority: &accounts + .cctp_token_messenger_minter_event_authority, + cctp_token_messenger_minter_custody_token: &accounts + .cctp_token_messenger_minter_custody_token, + cctp_token_messenger_minter_program: &accounts.cctp_token_messenger_minter_program, + cctp_message_transmitter_program: &accounts.cctp_message_transmitter_program, + guardian_set: &accounts.guardian_set, + guardian_set_signatures: &accounts.guardian_set_signatures, + verify_shim_program: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + token_program: &spl_token::ID, + system_program: &solana_program::system_program::ID, + }; + + let finalized_vaa_message_args = data.finalized_vaa_message_args; + let data = PrepareOrderResponseCctpShimData { + encoded_cctp_message: data.encoded_cctp_message, + cctp_attestation: data.cctp_attestation, + finalized_vaa_message_args, + }; + let program_id = &testing_context.get_matching_engine_program_id(); + let prepare_order_response_cctp_shim_ix = PrepareOrderResponseCctpShimIx { + program_id, + accounts: ix_accounts, + data, + } + .instruction(); + + let recent_blockhash = testing_context + .get_new_latest_blockhash(test_context) + .await + .expect("Failed to get new latest blockhash"); + let transaction = Transaction::new_signed_with_payer( + &[prepare_order_response_cctp_shim_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + recent_blockhash, + ); + + let expected_error = config.expected_error(); + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + if config.expected_error.is_none() { + let auction_accounts = config + .overwrite_auction_accounts + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_accounts() + .expect("Auction accounts not found") + }); + + let order_prepared_state = OrderPreparedState { + prepared_order_response_address: accounts.prepared_order_response, + prepared_custody_token: accounts.prepared_custody_token, + base_fee_token: accounts.base_fee_token, + actor_enum: config.actor_enum, + }; + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } +} + +/// Prepare order response shim data helper +/// +/// This struct is a helper struct used to create the data for the prepare order response instruction +/// +/// # Fields +/// +/// * `encoded_cctp_message` - The encoded CCTP message +/// * `cctp_attestation` - The CCTP attestation +/// * `finalized_vaa_message_args` - The finalized VAA message args +/// * `fast_market_order` - The fast market order +struct PrepareOrderResponseShimDataHelper { + pub encoded_cctp_message: Vec, + pub cctp_attestation: Vec, + pub finalized_vaa_message_args: FinalizedVaaMessageArgs, + pub fast_market_order: FastMarketOrderState, + pub guardian_signature_info: GuardianSignatureInfo, +} + +/// A helper struct for the data for the prepare order response shimful instruction that disregards the lifetime +impl PrepareOrderResponseShimDataHelper { + /// Create a new prepare order response shim data helper + /// + /// # Arguments + /// + /// * `encoded_cctp_message` - The encoded CCTP message + /// * `cctp_attestation` - The CCTP attestation + /// * `consistency_level` - The consistency level + /// * `base_fee` - The base fee + /// * `fast_market_order` - The fast market order + /// * `guardian_set_bump` - The guardian set bump + /// + /// # Returns + /// + /// The prepare order response shim data helper + pub async fn new( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &PrepareOrderResponseInstructionConfig, + ) -> Self { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let deposit_vaa = current_state + .get_test_vaa_pair(config.vaa_index) + .deposit_vaa + .clone(); + let deposit_vaa_data = deposit_vaa.get_vaa_data(); + let deposit = deposit_vaa + .payload_deserialized + .clone() + .unwrap() + .get_deposit() + .unwrap(); + let finalized_vaa_data = current_state + .get_test_vaa_pair(config.vaa_index) + .get_finalized_vaa_data() + .clone(); + + let fast_market_order_state = current_state + .fast_market_order() + .expect("could not find fast market order") + .fast_market_order; + + let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( + testing_context, + test_context, + current_state, + config.vaa_index, + ) + .await + .unwrap(); + cctp_token_burn_message + .verify_cctp_message(&fast_market_order_state) + .unwrap(); + + let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); + + let core_bridge_program_id = &testing_context.get_wormhole_program_id(); + + let guardian_signature_info = super::verify_shim::create_guardian_signatures( + testing_context, + test_context, + &payer_signer, + &finalized_vaa_data, + core_bridge_program_id, + None, + ) + .await + .unwrap(); + + Self { + encoded_cctp_message: cctp_token_burn_message.encoded_cctp_burn_message, + cctp_attestation: cctp_token_burn_message.cctp_attestation, + finalized_vaa_message_args: FinalizedVaaMessageArgs { + consistency_level: deposit_vaa_data.consistency_level, + base_fee: deposit_base_fee, + guardian_set_bump: guardian_signature_info.guardian_set_bump, + }, + fast_market_order: fast_market_order_state, + guardian_signature_info, + } + } + pub fn decode_cctp_message(&self) -> CctpMessageDecoded { + let cctp_message_decoded = CctpMessage::parse(&self.encoded_cctp_message[..]).unwrap(); + CctpMessageDecoded { + nonce: cctp_message_decoded.nonce(), + source_domain: cctp_message_decoded.source_domain(), + } + } +} + +/// Prepare order response shim accounts helper +/// +/// A helper struct for the accounts for the prepare order response shimful instruction that disregards the lifetime +/// +/// Fields are equivalent to the PrepareOrderResponseCctpShimAccounts struct +struct PrepareOrderResponseShimAccountsHelper { pub signer: Pubkey, pub custodian: Pubkey, pub fast_market_order: Pubkey, @@ -50,17 +297,32 @@ pub struct PrepareOrderResponseShimAccountsFixture { pub cctp_token_messenger_minter_event_authority: Pubkey, pub guardian_set: Pubkey, pub guardian_set_signatures: Pubkey, + pub prepared_order_response: Pubkey, + pub prepared_custody_token: Pubkey, } -impl PrepareOrderResponseShimAccountsFixture { +impl PrepareOrderResponseShimAccountsHelper { + /// Create a new prepare order response shim accounts helper + /// + /// # Arguments + /// + /// * `testing_context` - The testing context + /// * `config` - The prepare order response instruction config + /// * `current_state` - The current state + /// * `cctp_message_decoded` - The CCTP message decoded + /// * `data` - The prepare order response shim data helper pub fn new( testing_context: &TestingContext, config: &PrepareOrderResponseInstructionConfig, current_state: &TestingEngineState, - signer: &Pubkey, cctp_message_decoded: &CctpMessageDecoded, - guardian_signature_info: &GuardianSignatureInfo, + data: &PrepareOrderResponseShimDataHelper, ) -> Self { + let guardian_signature_info = &data.guardian_signature_info; + let signer = &config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let usdc_mint_address = testing_context.get_usdc_mint_address(); let auction_accounts = config .overwrite_auction_accounts @@ -117,8 +379,27 @@ impl PrepareOrderResponseShimAccountsFixture { } _ => panic!("Unsupported transfer direction"), }; + let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); + let fast_market_order_digest = data.fast_market_order.digest(); + let prepared_order_response_seeds = [ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_digest, + ]; + + let (prepared_order_response_pda, _prepared_order_response_bump) = + Pubkey::find_program_address( + &prepared_order_response_seeds, + matching_engine_program_id, + ); + + let prepared_custody_token_seeds = [ + matching_engine::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_pda.as_ref(), + ]; + let (prepared_custody_token_pda, _prepared_custody_token_bump) = + Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); Self { - signer: *signer, + signer: signer.pubkey(), custodian, fast_market_order, from_endpoint, @@ -141,282 +422,8 @@ impl PrepareOrderResponseShimAccountsFixture { cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority, guardian_set: guardian_signature_info.guardian_set_pubkey, guardian_set_signatures: guardian_signature_info.guardian_signatures_pubkey, - } - } -} - -/// Prepare order response shim data helper -/// -/// This struct is a helper struct used to create the data for the prepare order response instruction -/// -/// # Fields -/// -/// * `encoded_cctp_message` - The encoded CCTP message -/// * `cctp_attestation` - The CCTP attestation -/// * `finalized_vaa_message_args` - The finalized VAA message args -/// * `fast_market_order` - The fast market order -pub struct PrepareOrderResponseShimDataHelper { - pub encoded_cctp_message: Vec, - pub cctp_attestation: Vec, - pub finalized_vaa_message_args: FinalizedVaaMessageArgs, - pub fast_market_order: FastMarketOrderState, -} - -impl PrepareOrderResponseShimDataHelper { - /// Create a new prepare order response shim data helper - /// - /// # Arguments - /// - /// * `encoded_cctp_message` - The encoded CCTP message - /// * `cctp_attestation` - The CCTP attestation - /// * `consistency_level` - The consistency level - /// * `base_fee` - The base fee - /// * `fast_market_order` - The fast market order - /// * `guardian_set_bump` - The guardian set bump - /// - /// # Returns - /// - /// The prepare order response shim data helper - pub fn new( - encoded_cctp_message: Vec, - cctp_attestation: Vec, - consistency_level: u8, - base_fee: u64, - fast_market_order: &FastMarketOrderState, - guardian_set_bump: u8, - ) -> Self { - Self { - encoded_cctp_message, - cctp_attestation, - finalized_vaa_message_args: FinalizedVaaMessageArgs { - consistency_level, - base_fee, - guardian_set_bump, - }, - fast_market_order: *fast_market_order, - } - } - pub fn decode_cctp_message(&self) -> CctpMessageDecoded { - let cctp_message_decoded = CctpMessage::parse(&self.encoded_cctp_message[..]).unwrap(); - CctpMessageDecoded { - nonce: cctp_message_decoded.nonce(), - source_domain: cctp_message_decoded.source_domain(), - } - } -} - -/// Executes the instruction that prepares the order response for the CCTP shim -/// -/// # Arguments -/// -/// * `testing_context` - The testing context -/// * `test_context` - The test context -/// * `payer_signer` - The payer signer -/// * `accounts` - The prepare order response shim accounts -/// * `data` - The prepare order response shim data -/// * `expected_error` - The expected error -/// -/// # Returns -/// -/// The prepare order response shim fixture -pub async fn prepare_order_response_cctp_shim( - testing_context: &TestingContext, - test_context: &mut ProgramTestContext, - payer_signer: &Rc, - accounts: PrepareOrderResponseShimAccountsFixture, - data: PrepareOrderResponseShimDataHelper, - expected_error: Option<&ExpectedError>, -) -> Option { - let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); - let fast_market_order_digest = data.fast_market_order.digest(); - let prepared_order_response_seeds = [ - PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_digest, - ]; - - let (prepared_order_response_pda, _prepared_order_response_bump) = - Pubkey::find_program_address(&prepared_order_response_seeds, matching_engine_program_id); - - let prepared_custody_token_seeds = [ - matching_engine::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, - prepared_order_response_pda.as_ref(), - ]; - let (prepared_custody_token_pda, _prepared_custody_token_bump) = - Pubkey::find_program_address(&prepared_custody_token_seeds, matching_engine_program_id); - - let ix_accounts = PrepareOrderResponseCctpShimAccounts { - signer: &accounts.signer, - custodian: &accounts.custodian, - fast_market_order: &accounts.fast_market_order, - from_endpoint: &accounts.from_endpoint, - to_endpoint: &accounts.to_endpoint, - prepared_order_response: &prepared_order_response_pda, - prepared_custody_token: &prepared_custody_token_pda, - base_fee_token: &accounts.base_fee_token, - usdc: &accounts.usdc, - cctp_mint_recipient: &accounts.cctp_mint_recipient, - cctp_message_transmitter_authority: &accounts.cctp_message_transmitter_authority, - cctp_message_transmitter_config: &accounts.cctp_message_transmitter_config, - cctp_used_nonces: &accounts.cctp_used_nonces, - cctp_message_transmitter_event_authority: &accounts - .cctp_message_transmitter_event_authority, - cctp_token_messenger: &accounts.cctp_token_messenger, - cctp_remote_token_messenger: &accounts.cctp_remote_token_messenger, - cctp_token_minter: &accounts.cctp_token_minter, - cctp_local_token: &accounts.cctp_local_token, - cctp_token_pair: &accounts.cctp_token_pair, - cctp_token_messenger_minter_event_authority: &accounts - .cctp_token_messenger_minter_event_authority, - cctp_token_messenger_minter_custody_token: &accounts - .cctp_token_messenger_minter_custody_token, - cctp_token_messenger_minter_program: &accounts.cctp_token_messenger_minter_program, - cctp_message_transmitter_program: &accounts.cctp_message_transmitter_program, - guardian_set: &accounts.guardian_set, - guardian_set_signatures: &accounts.guardian_set_signatures, - verify_shim_program: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - token_program: &spl_token::ID, - system_program: &solana_program::system_program::ID, - }; - - let finalized_vaa_message_args = data.finalized_vaa_message_args; - let data = PrepareOrderResponseCctpShimData { - encoded_cctp_message: data.encoded_cctp_message, - cctp_attestation: data.cctp_attestation, - finalized_vaa_message_args, - }; - - let prepare_order_response_cctp_shim_ix = PrepareOrderResponseCctpShimIx { - program_id: matching_engine_program_id, - accounts: ix_accounts, - data, - } - .instruction(); - - let recent_blockhash = testing_context - .get_new_latest_blockhash(test_context) - .await - .expect("Failed to get new latest blockhash"); - let transaction = Transaction::new_signed_with_payer( - &[prepare_order_response_cctp_shim_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - recent_blockhash, - ); - testing_context - .execute_and_verify_transaction(test_context, transaction, expected_error) - .await; - if expected_error.is_none() { - Some(PrepareOrderResponseShimFixture { prepared_order_response: prepared_order_response_pda, prepared_custody_token: prepared_custody_token_pda, - base_fee_token: accounts.base_fee_token, - }) - } else { - None + } } } - -/// Prepare order response test -/// -/// Executes the prepare order response instruction in a testing context -/// -/// # Arguments -/// -/// * `testing_context` - The testing context -/// * `test_context` - The test context -/// * `config` - The prepare order response instruction config -/// * `current_state` - The current state -/// -/// # Returns -/// -/// The prepare order response shim fixture (none if failed) -pub async fn prepare_order_response_test( - testing_context: &TestingContext, - test_context: &mut ProgramTestContext, - config: &PrepareOrderResponseInstructionConfig, - current_state: &TestingEngineState, -) -> Option { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let deposit_vaa = current_state - .get_test_vaa_pair(config.vaa_index) - .deposit_vaa - .clone(); - let deposit_vaa_data = deposit_vaa.get_vaa_data(); - let deposit = deposit_vaa - .payload_deserialized - .clone() - .unwrap() - .get_deposit() - .unwrap(); - let core_bridge_program_id = &testing_context.get_wormhole_program_id(); - - let finalized_vaa_data = current_state - .get_test_vaa_pair(config.vaa_index) - .get_finalized_vaa_data() - .clone(); - - let guardian_signature_info = super::verify_shim::create_guardian_signatures( - testing_context, - test_context, - &payer_signer, - &finalized_vaa_data, - core_bridge_program_id, - None, - ) - .await - .unwrap(); - - let fast_market_order_state = current_state - .fast_market_order() - .expect("could not find fast market order") - .fast_market_order; - - let cctp_token_burn_message = utils::cctp_message::craft_cctp_token_burn_message( - testing_context, - test_context, - current_state, - config.vaa_index, - ) - .await - .unwrap(); - cctp_token_burn_message - .verify_cctp_message(&fast_market_order_state) - .unwrap(); - - let deposit_base_fee = utils::cctp_message::get_deposit_base_fee(&deposit); - let prepare_order_response_cctp_shim_data = PrepareOrderResponseShimDataHelper::new( - cctp_token_burn_message.encoded_cctp_burn_message, - cctp_token_burn_message.cctp_attestation, - deposit_vaa_data.consistency_level, - deposit_base_fee, - &fast_market_order_state, - guardian_signature_info.guardian_set_bump, - ); - let cctp_message_decoded = prepare_order_response_cctp_shim_data.decode_cctp_message(); - let prepare_order_response_cctp_shim_accounts = PrepareOrderResponseShimAccountsFixture::new( - testing_context, - config, - current_state, - &payer_signer.pubkey(), - &cctp_message_decoded, - &guardian_signature_info, - ); - super::shims_prepare_order_response::prepare_order_response_cctp_shim( - testing_context, - test_context, - &payer_signer, - prepare_order_response_cctp_shim_accounts, - prepare_order_response_cctp_shim_data, - config.expected_error(), - ) - .await -} - -pub struct PrepareOrderResponseShimFixture { - pub prepared_order_response: Pubkey, - pub prepared_custody_token: Pubkey, - pub base_fee_token: Pubkey, -} diff --git a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs index 8632feb71..81cd80b22 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/verify_shim.rs @@ -18,6 +18,13 @@ use std::str::FromStr; use wormhole_svm_definitions::GUARDIAN_SIGNATURE_LENGTH; use wormhole_svm_shim::verify_vaa; +/// Guardian signature info +/// +/// # Fields +/// +/// * `guardian_set_pubkey` - The guardian set pubkey +/// * `guardian_signatures_pubkey` - The guardian signatures pubkey +/// * `guardian_set_bump` - The guardian set bump pub struct GuardianSignatureInfo { pub guardian_set_pubkey: Pubkey, pub guardian_signatures_pubkey: Pubkey, diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 5a57fef95..a774b407e 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -181,9 +181,12 @@ pub async fn initialize_program( test_context, &[instruction], Some(&payer_signer.pubkey()), - &[&payer_signer, &testing_context.testing_actors.owner.keypair()], - 1000000000, - 1000000000, + &[ + &payer_signer, + &testing_context.testing_actors.owner.keypair(), + ], + None, + None, ) .await; // Process transaction @@ -200,9 +203,12 @@ pub async fn initialize_program( test_context, &[instruction], Some(&payer_signer.pubkey()), - &[&payer_signer, &testing_context.testing_actors.owner.keypair()], - 1000000000, - 1000000000, + &[ + &payer_signer, + &testing_context.testing_actors.owner.keypair(), + ], + None, + None, ) .await; let versioned_transaction = VersionedTransaction::from(transaction); diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs index 5855f82f2..672f5c40f 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/execute_order.rs @@ -970,7 +970,7 @@ pub async fn test_execute_order_shim_after_close_fast_market_order_fails() { ) .await; let expected_error = ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: 3001, // Account Discriminator not found error_string: "AccountDiscriminatorNotFound.".to_string(), }; @@ -1081,7 +1081,7 @@ pub async fn test_execute_order_shim_emitter_chain_mismatch() { ExecuteOrderInstructionConfig { vaa_index: 1, expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::VaaMismatch), error_string: "AccountNotInitialized".to_string(), }), @@ -1112,7 +1112,7 @@ pub async fn test_execute_order_shim_before_auction_duration_is_over() { ExecuteOrderInstructionConfig { fast_forward_slots: 0, expected_error: Some(ExpectedError { - instruction_index: 0, + instruction_index: 2, error_code: u32::from(MatchingEngineError::AuctionPeriodNotExpired), error_string: "AuctionPeriodNotExpired".to_string(), }), diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index f4384e8fb..fe61ab4ce 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -52,7 +52,7 @@ pub type OverwriteCurrentState = Option; /// /// # Fields /// -/// * `instruction_index` - The index of the instruction that is expected to error +/// * `instruction_index` - The index of the instruction that is expected to error. Because of how the transaction is built in the testing engine, the instruction index is always at least 2. /// * `error_code` - The error code that is expected to be returned /// * `error_string` - A description of the error that is expected to be returned for debugging purposes // TODO: Change the error string to either be checked for or change the field name AND make it optional diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 9f90b3bea..e95055b56 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -343,7 +343,7 @@ impl TestingEngine { .await } InstructionTrigger::ExecuteOrderShim(ref config) => { - self.execute_order_shim(test_context, current_state, config) + self.execute_order_shimful(test_context, current_state, config) .await } InstructionTrigger::ExecuteOrderShimless(ref config) => { @@ -638,43 +638,19 @@ impl TestingEngine { } /// Instruction trigger function for executing an order - async fn execute_order_shim( + async fn execute_order_shimful( &self, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &ExecuteOrderInstructionConfig, ) -> TestingEngineState { - let result = shimful::shims_execute_order::execute_order_shimful_test( + shimful::shims_execute_order::execute_order_shimful( &self.testing_context, test_context, current_state, config, ) - .await; - if config.expected_error.is_none() { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - let order_executed_fallback_fixture = result.unwrap(); - let order_executed_state = OrderExecutedState { - cctp_message: order_executed_fallback_fixture.cctp_message, - post_message_sequence: Some(order_executed_fallback_fixture.post_message_sequence), - post_message_message: Some(order_executed_fallback_fixture.post_message_message), - actor_enum: config.actor_enum, - }; - TestingEngineState::OrderExecuted { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_executed: order_executed_state, - auction_accounts: auction_accounts.clone(), - order_prepared: current_state.order_prepared().cloned(), - } - } else { - current_state.clone() - } + .await } /// Instruction trigger function for executing an order @@ -750,43 +726,13 @@ impl TestingEngine { current_state: &TestingEngineState, config: &PrepareOrderResponseInstructionConfig, ) -> TestingEngineState { - let result = shimful::shims_prepare_order_response::prepare_order_response_test( + shimful::shims_prepare_order_response::prepare_order_response_cctp_shimful( &self.testing_context, test_context, config, current_state, ) - .await; - if config.expected_error.is_none() { - let auction_accounts = - config - .overwrite_auction_accounts - .as_ref() - .unwrap_or_else(|| { - current_state - .auction_accounts() - .expect("Auction accounts not found") - }); - let prepare_order_response_fixture = result.unwrap(); - let order_prepared_state = OrderPreparedState { - prepared_order_response_address: prepare_order_response_fixture - .prepared_order_response, - prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - base_fee_token: prepare_order_response_fixture.base_fee_token, - actor_enum: config.actor_enum, - }; - TestingEngineState::OrderPrepared { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_prepared: order_prepared_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } + .await } /// Instruction trigger function for preparing an order @@ -1003,11 +949,9 @@ impl TestingEngine { place_initial_offer_instruction, ], Some(&place_initial_offer_payer_signer.pubkey()), - &[ - &place_initial_offer_payer_signer, - ], - 1000000000, - 1000000000, + &[&place_initial_offer_payer_signer], + None, + None, ) .await; let actor_usdc_balance_before = place_initial_offer_config diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs index 997e5b3d0..c913bbd53 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/setup.rs @@ -157,6 +157,8 @@ impl PreTestingContext { } } +// TODO: Move the testing context to a different module + /// Testing Context struct that stores common data needed to run tests /// /// # Fields @@ -398,9 +400,11 @@ impl TestingContext { instructions: &[Instruction], payer: Option<&Pubkey>, signers: &[&Keypair], - compute_unit_price: u64, - compute_unit_limit: u32, + compute_unit_price: Option, + compute_unit_limit: Option, ) -> Transaction { + let compute_unit_price = compute_unit_price.unwrap_or_else(|| 1000000000); + let compute_unit_limit = compute_unit_limit.unwrap_or_else(|| 1000000000); let last_blockhash = self.get_new_latest_blockhash(test_context).await.unwrap(); let compute_budget_price = ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price); @@ -442,7 +446,7 @@ impl TestingContext { instruction_index, expected_error.instruction_index, "Expected error on instruction {}, but got: {:?}", expected_error.instruction_index, tx_error - ); + ); match instruction_error { InstructionError::Custom(error_code) => { assert_eq!( @@ -1088,7 +1092,7 @@ impl TestingActors { } /// Creates USDC associated token accounts - /// + /// /// Creates usdc associated token accounts for all actors that expect to have them /// /// # Arguments diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 0fd826465..0c5683e35 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -421,7 +421,7 @@ impl ActiveAuctionState { /// /// # Fields /// -/// * `participant` - The participant of the offer +/// * `participant` - The participant of the offer (the signer of the transaction) /// * `offer_token` - The token of the offer /// * `offer_price` - The price of the offer #[derive(Clone, Default)] From a413abf348b2f50b68b7bdeb193ba0fc15f473d5 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 9 May 2025 16:03:46 +0100 Subject: [PATCH 090/112] refactored and docstringed matching-engine-testing/tests/shimless directory --- .../tests/shimless/execute_order.rs | 186 ++++++---- .../tests/shimless/initialize.rs | 327 ++++++++++-------- .../tests/shimless/make_offer.rs | 92 +++-- .../tests/shimless/prepare_order_response.rs | 137 +++++--- .../tests/shimless/settle_auction.rs | 15 +- .../tests/testing_engine/engine.rs | 134 +------ .../tests/utils/auction.rs | 4 +- 7 files changed, 466 insertions(+), 429 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs index 7e01382cd..19923ba03 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/execute_order.rs @@ -1,8 +1,8 @@ use std::rc::Rc; -use crate::testing_engine::config::{ExecuteOrderInstructionConfig, ExpectedError}; +use crate::testing_engine::config::{ExecuteOrderInstructionConfig, InstructionConfig}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; -use crate::utils::account_fixtures::FixtureAccounts; +use crate::testing_engine::state::{OrderExecutedState, TestingEngineState}; use crate::utils::auction::{AuctionAccounts, AuctionState}; use anchor_lang::prelude::*; use anchor_lang::{InstructionData, ToAccountMetas}; @@ -22,18 +22,123 @@ use solana_sdk::sysvar::SysvarId; use solana_sdk::transaction::Transaction; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; -pub struct ExecuteOrderShimlessFixture { - pub cctp_message: Pubkey, +/// Execute order shimless +/// +/// Helper function to execute an order using the shimless method +/// +/// # Arguments +/// +/// * `testing_context`: The testing context of the testing engine +/// * `test_context`: A mutable reference to the test context +/// * `current_state`: The current state of the testing engine +/// * `config`: The execute order instruction config +/// * `auction_accounts`: The auction accounts +/// +/// # Returns +/// +/// The new state of the testing engine +pub async fn execute_order_shimless( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &ExecuteOrderInstructionConfig, + auction_accounts: &AuctionAccounts, +) -> TestingEngineState { + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let auction_state = current_state.auction_state(); + let slots_to_fast_forward = config.fast_forward_slots; + if slots_to_fast_forward > 0 { + crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) + .await; + } + let execute_order_accounts: ExecuteOrderShimlessAccounts = + create_execute_order_shimless_accounts( + testing_context, + auction_accounts, + &payer_signer, + auction_state, + config, + ); + let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); + let execute_order_ix = Instruction { + program_id: testing_context.get_matching_engine_program_id(), + accounts: execute_order_accounts.to_account_metas(None), + data: execute_order_instruction_data, + }; + let tx = Transaction::new_signed_with_payer( + &[execute_order_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(), + ); + let expected_error = config.expected_error(); + testing_context + .execute_and_verify_transaction(test_context, tx, expected_error) + .await; + if config.expected_error.is_none() { + let order_executed_state = OrderExecutedState { + cctp_message: execute_order_accounts.cctp_message, + post_message_sequence: None, + post_message_message: None, + actor_enum: config.actor_enum, + }; + TestingEngineState::OrderExecuted { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_executed: order_executed_state, + auction_accounts: auction_accounts.clone(), + order_prepared: current_state.order_prepared().cloned(), + } + } else { + current_state.clone() + } } -pub fn create_execute_order_shimless_accounts( +/// Create execute order shimless accounts +/// +/// Helper function to create the accounts needed for the execute order instruction +/// +/// # Arguments +/// +/// * `testing_context`: The testing context +/// * `auction_accounts`: The auction accounts +/// * `payer_signer`: The payer signer +/// * `auction_state`: The auction state +/// * `config`: The execute order instruction config +/// +/// # Returns +/// +/// The execute order shimless accounts +fn create_execute_order_shimless_accounts( testing_context: &TestingContext, - fixture_accounts: &FixtureAccounts, auction_accounts: &AuctionAccounts, payer_signer: &Rc, auction_state: &AuctionState, - executor_token: Pubkey, + config: &ExecuteOrderInstructionConfig, ) -> ExecuteOrderShimlessAccounts { + let fixture_accounts = testing_context + .get_fixture_accounts() + .expect("Fixture accounts not found"); + let executor_token = config + .actor_enum + .get_actor(&testing_context.testing_actors) + .token_account_address(&config.token_enum) + .unwrap_or_else(|| { + auction_state + .get_active_auction() + .unwrap() + .best_offer + .offer_token + }); let active_auction_state = auction_state.get_active_auction().unwrap(); let active_auction_address = active_auction_state.auction_address; let active_auction_custody_token = active_auction_state.auction_custody_token_address; @@ -152,70 +257,3 @@ pub fn create_execute_order_shimless_accounts( sysvars, } } - -pub async fn execute_order_shimless_test( - testing_context: &TestingContext, - test_context: &mut ProgramTestContext, - config: &ExecuteOrderInstructionConfig, - auction_accounts: &AuctionAccounts, - auction_state: &AuctionState, - expected_error: Option<&ExpectedError>, -) -> Option { - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); - let slots_to_fast_forward = config.fast_forward_slots; - if slots_to_fast_forward > 0 { - crate::testing_engine::engine::fast_forward_slots(test_context, slots_to_fast_forward) - .await; - } - let executor_token = config - .actor_enum - .get_actor(&testing_context.testing_actors) - .token_account_address(&config.token_enum) - .unwrap_or_else(|| { - auction_state - .get_active_auction() - .unwrap() - .best_offer - .offer_token - }); - let fixture_accounts = testing_context - .get_fixture_accounts() - .expect("Fixture accounts not found"); - let execute_order_accounts: ExecuteOrderShimlessAccounts = - create_execute_order_shimless_accounts( - testing_context, - &fixture_accounts, - auction_accounts, - &payer_signer, - auction_state, - executor_token, - ); - let execute_order_instruction_data = ExecuteOrderShimlessInstruction {}.data(); - let execute_order_ix = Instruction { - program_id: testing_context.get_matching_engine_program_id(), - accounts: execute_order_accounts.to_account_metas(None), - data: execute_order_instruction_data, - }; - let tx = Transaction::new_signed_with_payer( - &[execute_order_ix], - Some(&payer_signer.pubkey()), - &[&payer_signer], - testing_context - .get_new_latest_blockhash(test_context) - .await - .unwrap(), - ); - testing_context - .execute_and_verify_transaction(test_context, tx, expected_error) - .await; - if expected_error.is_none() { - Some(ExecuteOrderShimlessFixture { - cctp_message: execute_order_accounts.cctp_message, - }) - } else { - None - } -} diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index a774b407e..52a510a47 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -10,6 +10,7 @@ use solana_program::{bpf_loader_upgradeable, system_program}; use crate::{ testing_engine::{ config::{InitializeInstructionConfig, InstructionConfig}, + setup::TestingActors, state::{InitializedState, TestingEngineState}, }, utils::token_account::SplTokenEnum, @@ -23,140 +24,20 @@ use matching_engine::{ InitializeArgs, }; -#[derive(Clone)] -pub struct InitializeAddresses { - pub custodian_address: Pubkey, - pub auction_config_address: Pubkey, - pub cctp_mint_recipient: Pubkey, -} - -impl InitializeAddresses { - pub fn create( - testing_context: &TestingContext, - auction_parameters_config: &AuctionParametersConfig, - ) -> Self { - let program_id = testing_context.get_matching_engine_program_id(); - let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); - let (custodian, _custodian_bump) = - Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); - - let (auction_config, _auction_config_bump) = Pubkey::find_program_address( - &[ - AuctionConfig::SEED_PREFIX, - &auction_parameters_config.config_id.to_be_bytes(), - ], - &program_id, - ); - - Self { - custodian_address: custodian, - auction_config_address: auction_config, - cctp_mint_recipient, - } - } -} - -pub struct InitializeFixture { - pub custodian: Custodian, - pub addresses: InitializeAddresses, -} - -#[derive(Debug, PartialEq, Eq)] -struct TestCustodian { - owner: Pubkey, - pending_owner: Option, - paused: bool, - paused_set_by: Pubkey, - owner_assistant: Pubkey, - fee_recipient_token: Pubkey, - auction_config_id: u32, - next_proposal_id: u64, -} - -impl From<&Custodian> for TestCustodian { - fn from(c: &Custodian) -> Self { - Self { - owner: c.owner, - pending_owner: c.pending_owner, - paused: c.paused, - paused_set_by: c.paused_set_by, - owner_assistant: c.owner_assistant, - fee_recipient_token: c.fee_recipient_token, - auction_config_id: c.auction_config_id, - next_proposal_id: c.next_proposal_id, - } - } -} - -impl InitializeFixture { - pub fn verify_custodian( - &self, - owner: Pubkey, - owner_assistant: Pubkey, - fee_recipient_token: Pubkey, - ) { - let expected_custodian = TestCustodian { - owner, - pending_owner: None, - paused: false, - paused_set_by: owner, - owner_assistant, - fee_recipient_token, - auction_config_id: 0, - next_proposal_id: 0, - }; - - let actual_custodian = TestCustodian::from(&self.custodian); - assert_eq!(actual_custodian, expected_custodian); - } -} - -#[derive(Clone)] -pub struct AuctionParametersConfig { - // Auction config iid used for seeding the auction config account - pub config_id: u32, - // Fields in the auction parameters account - pub user_penalty_reward_bps: u32, - pub initial_penalty_bps: u32, - pub duration: u16, - pub grace_period: u16, - pub penalty_period: u16, - pub min_offer_delta_bps: u32, - pub security_deposit_base: u64, - pub security_deposit_bps: u32, -} - -impl Default for AuctionParametersConfig { - fn default() -> Self { - Self { - config_id: 0, - user_penalty_reward_bps: 250_000, // 25% - initial_penalty_bps: 250_000, // 25% - duration: 2, - grace_period: 5, - penalty_period: 10, - min_offer_delta_bps: 20_000, // 2% - security_deposit_base: 4_200_000, - security_deposit_bps: 5_000, // 0.5% - } - } -} - -impl From<&AuctionParametersConfig> for AuctionParameters { - fn from(val: &AuctionParametersConfig) -> Self { - AuctionParameters { - user_penalty_reward_bps: val.user_penalty_reward_bps, - initial_penalty_bps: val.initial_penalty_bps, - duration: val.duration, - grace_period: val.grace_period, - penalty_period: val.penalty_period, - min_offer_delta_bps: val.min_offer_delta_bps, - security_deposit_base: val.security_deposit_base, - security_deposit_bps: val.security_deposit_bps, - } - } -} - +/// Initialize the program +/// +/// Initialize the program with the given configuration +/// +/// # Arguments +/// +/// * `testing_context`: The testing context of the testing engine +/// * `test_context`: Mutable reference to the program test context +/// * `initial_state`: The initial state of the testing engine +/// * `config`: The configuration for the initialize instruction +/// +/// # Returns +/// +/// The state of the testing engine after the initialize instruction pub async fn initialize_program( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -172,7 +53,7 @@ pub async fn initialize_program( .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); // Create the initialize addresses let initialize_addresses = - InitializeAddresses::create(testing_context, &auction_parameters_config); + InitializeAddresses::new(testing_context, &auction_parameters_config); // Create the initialize instruction let instruction = initialize_program_instruction(testing_context, &auction_parameters_config); // Create and sign transaction @@ -230,19 +111,8 @@ pub async fn initialize_program( .expect("Custodian account not found"); let custodian = Custodian::try_deserialize(&mut custodian_account.data.as_slice()).unwrap(); - let initialize_fixture = InitializeFixture { - custodian, - addresses: initialize_addresses.clone(), - }; - let testing_actors = &testing_context.testing_actors; - let owner = testing_actors.owner.pubkey(); - let owner_assistant = testing_actors.owner_assistant.pubkey(); - let fee_recipient_token = testing_actors - .fee_recipient - .token_account_address(&SplTokenEnum::Usdc) - .unwrap(); + verify_custodian(&custodian, &testing_context.testing_actors); - initialize_fixture.verify_custodian(owner, owner_assistant, fee_recipient_token); TestingEngineState::Initialized { base: initial_state.base().clone(), initialized: InitializedState { @@ -255,6 +125,18 @@ pub async fn initialize_program( } } +/// Initialize program instruction +/// +/// Create the initialize instruction for the program +/// +/// # Arguments +/// +/// * `testing_context`: The testing context of the testing engine +/// * `auction_parameters_config`: The configuration for the auction parameters +/// +/// # Returns +/// +/// The initialize instruction for the program pub fn initialize_program_instruction( testing_context: &TestingContext, auction_parameters_config: &AuctionParametersConfig, @@ -262,7 +144,7 @@ pub fn initialize_program_instruction( let program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = testing_context.get_usdc_mint_address(); let initialize_addresses = - InitializeAddresses::create(testing_context, &auction_parameters_config); + InitializeAddresses::new(testing_context, &auction_parameters_config); let InitializeAddresses { custodian_address: custodian, auction_config_address: auction_config, @@ -308,3 +190,152 @@ pub fn initialize_program_instruction( data: ix_data.data(), } } + +/// Initialize addresses +/// +/// All the addresses created by the initialize instruction +#[derive(Clone)] +pub struct InitializeAddresses { + pub custodian_address: Pubkey, + pub auction_config_address: Pubkey, + pub cctp_mint_recipient: Pubkey, +} + +impl InitializeAddresses { + pub fn new( + testing_context: &TestingContext, + auction_parameters_config: &AuctionParametersConfig, + ) -> Self { + let program_id = testing_context.get_matching_engine_program_id(); + let cctp_mint_recipient = testing_context.get_cctp_mint_recipient(); + let (custodian, _custodian_bump) = + Pubkey::find_program_address(&[Custodian::SEED_PREFIX], &program_id); + + let (auction_config, _auction_config_bump) = Pubkey::find_program_address( + &[ + AuctionConfig::SEED_PREFIX, + &auction_parameters_config.config_id.to_be_bytes(), + ], + &program_id, + ); + + Self { + custodian_address: custodian, + auction_config_address: auction_config, + cctp_mint_recipient, + } + } +} + +/// Test custodian +/// +/// A test custodian for verifying the initialized custodian +#[derive(Debug, PartialEq, Eq)] +struct TestCustodian { + owner: Pubkey, + pending_owner: Option, + paused: bool, + paused_set_by: Pubkey, + owner_assistant: Pubkey, + fee_recipient_token: Pubkey, + auction_config_id: u32, + next_proposal_id: u64, +} + +impl From<&Custodian> for TestCustodian { + fn from(c: &Custodian) -> Self { + Self { + owner: c.owner, + pending_owner: c.pending_owner, + paused: c.paused, + paused_set_by: c.paused_set_by, + owner_assistant: c.owner_assistant, + fee_recipient_token: c.fee_recipient_token, + auction_config_id: c.auction_config_id, + next_proposal_id: c.next_proposal_id, + } + } +} + +/// Verify custodian +/// +/// Verify the initialized custodian +/// +/// # Arguments +/// +/// * `custodian`: The initialized custodian +/// * `testing_actors`: The testing actors of the testing context of the testing engine +/// +/// # Returns +/// +/// The initialized custodian +fn verify_custodian(custodian: &Custodian, testing_actors: &TestingActors) { + let expected_custodian = TestCustodian { + owner: testing_actors.owner.pubkey(), + pending_owner: None, + paused: false, + paused_set_by: testing_actors.owner.pubkey(), + owner_assistant: testing_actors.owner_assistant.pubkey(), + fee_recipient_token: testing_actors + .fee_recipient + .token_account_address(&SplTokenEnum::Usdc) + .unwrap(), + auction_config_id: 0, + next_proposal_id: 0, + }; + + let actual_custodian = TestCustodian::from(custodian); + assert_eq!(actual_custodian, expected_custodian); +} + +/// Auction parameters config +/// +/// The configuration for the auction parameters +#[derive(Clone)] +pub struct AuctionParametersConfig { + // Auction config iid used for seeding the auction config account + pub config_id: u32, + // Fields in the auction parameters account + pub user_penalty_reward_bps: u32, + pub initial_penalty_bps: u32, + pub duration: u16, + pub grace_period: u16, + pub penalty_period: u16, + pub min_offer_delta_bps: u32, + pub security_deposit_base: u64, + pub security_deposit_bps: u32, +} + +impl Default for AuctionParametersConfig { + fn default() -> Self { + Self { + config_id: 0, + user_penalty_reward_bps: 250_000, // 25% + initial_penalty_bps: 250_000, // 25% + duration: 2, + grace_period: 5, + penalty_period: 10, + min_offer_delta_bps: 20_000, // 2% + security_deposit_base: 4_200_000, + security_deposit_bps: 5_000, // 0.5% + } + } +} + +/// Convert auction parameters config to auction parameters +/// +/// Convert the auction parameters config to an auction parameters account +impl From<&AuctionParametersConfig> for AuctionParameters { + fn from(val: &AuctionParametersConfig) -> Self { + AuctionParameters { + user_penalty_reward_bps: val.user_penalty_reward_bps, + initial_penalty_bps: val.initial_penalty_bps, + duration: val.duration, + grace_period: val.grace_period, + penalty_period: val.penalty_period, + min_offer_delta_bps: val.min_offer_delta_bps, + security_deposit_base: val.security_deposit_base, + security_deposit_bps: val.security_deposit_bps, + } + } +} diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index a3a8ce28a..26cd89f6b 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -1,11 +1,6 @@ -use std::rc::Rc; - -use crate::testing_engine::config::ExpectedError; use crate::testing_engine::config::ImproveOfferInstructionConfig; use crate::testing_engine::config::InstructionConfig; use crate::testing_engine::config::PlaceInitialOfferInstructionConfig; -use crate::testing_engine::setup::TestingActor; -use crate::testing_engine::state::InitialOfferPlacedState; use crate::testing_engine::state::TestingEngineState; use crate::utils::auction::AuctionAccounts; @@ -26,7 +21,6 @@ use matching_engine::instruction::{ use matching_engine::state::Auction; use solana_program_test::ProgramTestContext; use solana_sdk::instruction::Instruction; -use solana_sdk::signature::Keypair; use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; use utils::auction::{ActiveAuctionState, AuctionOffer, AuctionState}; @@ -37,23 +31,20 @@ use utils::auction::{ActiveAuctionState, AuctionOffer, AuctionState}; /// /// # Arguments /// -/// * `testing_context` - The testing context -/// * `test_context` - The test context -/// * `accounts` - The auction accounts -/// * `fast_market_order` - The fast market order -/// * `offer_price` - The price of the offer -/// * `payer_signer` - The payer signer -/// * `expected_error` - The expected error +/// * `testing_context`: The testing context of the testing engine +/// * `test_context`: Mutable reference to the program test context +/// * `current_state`: The current state of the testing engine +/// * `config`: The configuration for the place initial offer instruction /// /// # Returns /// -/// The new auction state if successful, otherwise the old auction state +/// The new state of the testing engine (if successful), otherwise the old state. pub async fn place_initial_offer_shimless( testing_context: &TestingContext, test_context: &mut ProgramTestContext, current_state: &TestingEngineState, config: &PlaceInitialOfferInstructionConfig, -) -> Option { +) -> TestingEngineState { let payer_signer = config .payer_signer .clone() @@ -280,13 +271,24 @@ pub async fn place_initial_offer_shimless( config.spl_token_enum.clone(), current_state.base().transfer_direction, ); - Some(InitialOfferPlacedState { + + auction_state + .get_active_auction() + .unwrap() + .verify_auction(&testing_context, test_context) + .await + .expect("Could not verify auction state"); + return TestingEngineState::InitialOfferPlaced { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), auction_state, auction_accounts, - }) - } else { - None + order_prepared: current_state.order_prepared().cloned(), + }; } + current_state.clone() } /// Improve an offer (shimless) @@ -295,26 +297,26 @@ pub async fn place_initial_offer_shimless( /// /// # Arguments /// -/// * `testing_context` - The testing context -/// * `test_context` - The test context -/// * `solver` - The solver -/// * `offer_price` - The new price -/// * `payer_signer` - The payer signer -/// * `initial_auction_state` - The initial auction state -/// * `expected_error` - The expected error +/// * `testing_context`: The testing context of the testing engine +/// * `test_context`: Mutable reference to the program test context +/// * `current_state`: The current state of the testing engine +/// * `config`: The configuration for the improve offer instruction /// /// # Returns /// -/// The new auction state if successful, otherwise the old auction state +/// The new state of the testing engine pub async fn improve_offer( testing_context: &TestingContext, test_context: &mut ProgramTestContext, - actor: &TestingActor, + current_state: &TestingEngineState, config: &ImproveOfferInstructionConfig, - payer_signer: &Rc, - initial_auction_state: &AuctionState, - expected_error: Option<&ExpectedError>, -) -> AuctionState { +) -> TestingEngineState { + let initial_auction_state = current_state.auction_state(); + let actor = config.actor.get_actor(&testing_context.testing_actors); + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let program_id = testing_context.get_matching_engine_program_id(); let active_auction_state = initial_auction_state.get_active_auction().unwrap(); let auction_config = active_auction_state.auction_config_address; @@ -376,13 +378,14 @@ pub async fn improve_offer( let tx = Transaction::new_signed_with_payer( &[improve_offer_ix_anchor], Some(&payer_signer.pubkey()), - &[payer_signer], + &[&payer_signer], testing_context .get_new_latest_blockhash(test_context) .await .unwrap(), ); + let expected_error = config.expected_error(); testing_context .execute_and_verify_transaction(test_context, tx, expected_error) .await; @@ -393,7 +396,7 @@ pub async fn improve_offer( .get_active_auction() .unwrap() .initial_offer; - AuctionState::Active(Box::new(ActiveAuctionState { + let new_auction_state = AuctionState::Active(Box::new(ActiveAuctionState { auction_address, auction_custody_token_address, auction_config_address: auction_config, @@ -405,8 +408,23 @@ pub async fn improve_offer( offer_price, }, spl_token_enum: spl_token_enum.clone(), - })) - } else { - initial_auction_state.clone() + })); + + new_auction_state + .get_active_auction() + .unwrap() + .verify_auction(&testing_context, test_context) + .await + .expect("Could not verify auction state"); + return TestingEngineState::OfferImproved { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: new_auction_state, + auction_accounts: current_state.auction_accounts().cloned(), + order_prepared: current_state.order_prepared().cloned(), + }; } + current_state.clone() } diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index ebe34d129..58d9a5f69 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -1,6 +1,6 @@ use crate::testing_engine::config::{InstructionConfig, PrepareOrderResponseInstructionConfig}; use crate::testing_engine::setup::{TestingContext, TransferDirection}; -use crate::testing_engine::state::TestingEngineState; +use crate::testing_engine::state::{OrderPreparedState, TestingEngineState}; use crate::utils; use crate::utils::cctp_message::UsedNonces; use anchor_lang::InstructionData; @@ -23,12 +23,6 @@ use solana_sdk::signature::Signer; use solana_sdk::transaction::Transaction; use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; -pub struct PrepareOrderResponseFixture { - pub prepared_order_response: Pubkey, - pub prepared_custody_token: Pubkey, - pub base_fee_token: Pubkey, -} - /// Prepare an order response (shimless) /// /// Prepare an order response by providing a fast market order. @@ -43,14 +37,13 @@ pub struct PrepareOrderResponseFixture { /// /// # Returns /// -/// The prepared order response fixture if successful, otherwise None +/// The new state after the prepare order response instruction is executed pub async fn prepare_order_response( testing_context: &TestingContext, test_context: &mut ProgramTestContext, config: &PrepareOrderResponseInstructionConfig, current_state: &TestingEngineState, - base_fee_token_address: &Pubkey, -) -> Option { +) -> TestingEngineState { let auction_accounts = config .overwrite_auction_accounts .as_ref() @@ -59,14 +52,94 @@ pub async fn prepare_order_response( .auction_accounts() .expect("Auction accounts not found") }); - let to_endpoint_address = &auction_accounts.to_router_endpoint; - let from_endpoint_address = &auction_accounts.from_router_endpoint; + let payer_signer = config .payer_signer .clone() .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let (prepare_order_response_ix, order_prepared_state) = + prepare_order_response_shimless_instruction( + testing_context, + test_context, + config, + current_state, + ) + .await; + + let transaction = Transaction::new_signed_with_payer( + &[prepare_order_response_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + testing_context + .get_new_latest_blockhash(test_context) + .await + .expect("Failed to get new blockhash"), + ); let expected_error = config.expected_error(); let expected_log_messages = config.expected_log_messages(); + if let Some(expected_log_messages) = expected_log_messages { + testing_context + .simulate_and_verify_logs(test_context, transaction, expected_log_messages) + .await + .unwrap(); + } else { + testing_context + .execute_and_verify_transaction(test_context, transaction, expected_error) + .await; + } + if config.expected_error.is_none() { + TestingEngineState::OrderPrepared { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + fast_market_order: current_state.fast_market_order().cloned(), + auction_state: current_state.auction_state().clone(), + order_prepared: order_prepared_state, + auction_accounts: auction_accounts.clone(), + } + } else { + current_state.clone() + } +} + +/// Create the prepare order response instruction and order prepared state +/// +/// # Arguments +/// +/// * `testing_context` - The testing context +/// * `test_context` - The test context +/// * `config` - The prepare order response instruction config +/// * `current_state` - The current state +/// +/// # Returns +/// +/// The prepare order response instruction and order prepared state +pub async fn prepare_order_response_shimless_instruction( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + config: &PrepareOrderResponseInstructionConfig, + current_state: &TestingEngineState, +) -> (Instruction, OrderPreparedState) { + let auction_accounts = config + .overwrite_auction_accounts + .as_ref() + .unwrap_or_else(|| { + current_state + .auction_accounts() + .expect("Auction accounts not found") + }); + let base_fee_token_address = config + .actor_enum + .get_actor(&testing_context.testing_actors) + .token_account_address(&config.token_enum) + .expect("Token account does not exist for solver at index"); + let to_endpoint_address = &auction_accounts.to_router_endpoint; + let from_endpoint_address = &auction_accounts.from_router_endpoint; + let payer_signer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + let matching_engine_program_id = &testing_context.get_matching_engine_program_id(); let usdc_mint_address = &testing_context.get_usdc_mint_address(); let cctp_mint_recipient = &testing_context.get_cctp_mint_recipient(); @@ -194,7 +267,7 @@ pub async fn prepare_order_response( finalized_vaa, prepared_order_response: prepared_order_response_pda, prepared_custody_token: prepared_custody_token_pda, - base_fee_token: *base_fee_token_address, + base_fee_token: base_fee_token_address, usdc, cctp, token_program: spl_token::ID, @@ -209,38 +282,16 @@ pub async fn prepare_order_response( } .data(); - let instruction = Instruction { + let ix = Instruction { program_id: *matching_engine_program_id, accounts: prepared_order_response_accounts.to_account_metas(None), data: prepare_order_response_ix_data, }; - - let transaction = Transaction::new_signed_with_payer( - &[instruction], - Some(&payer_signer.pubkey()), - &[&payer_signer], - testing_context - .get_new_latest_blockhash(test_context) - .await - .expect("Failed to get new blockhash"), - ); - if let Some(expected_log_messages) = expected_log_messages { - testing_context - .simulate_and_verify_logs(test_context, transaction, expected_log_messages) - .await - .unwrap(); - } else { - testing_context - .execute_and_verify_transaction(test_context, transaction, expected_error) - .await; - } - if expected_error.is_none() { - Some(PrepareOrderResponseFixture { - prepared_order_response: prepared_order_response_pda, - prepared_custody_token: prepared_custody_token_pda, - base_fee_token: *base_fee_token_address, - }) - } else { - None - } + let order_prepared_state = OrderPreparedState { + prepared_order_response_address: prepared_order_response_pda, + prepared_custody_token: prepared_custody_token_pda, + base_fee_token: base_fee_token_address, + actor_enum: config.actor_enum, + }; + (ix, order_prepared_state) } diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 68f890446..ae26387e6 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -39,7 +39,7 @@ pub async fn settle_auction_complete( test_context: &mut ProgramTestContext, config: &SettleAuctionInstructionConfig, expected_error: Option<&ExpectedError>, -) -> AuctionState { +) -> TestingEngineState { let payer_signer = &config .payer_signer .clone() @@ -102,8 +102,17 @@ pub async fn settle_auction_complete( .execute_and_verify_transaction(test_context, tx, expected_error) .await; if expected_error.is_none() { - AuctionState::Settled + TestingEngineState::AuctionSettled { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + auction_state: AuctionState::Settled(Box::new(active_auction.clone())), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: current_state.order_prepared().unwrap().clone(), + auction_accounts: current_state.auction_accounts().cloned(), + order_executed: current_state.order_executed().cloned(), + } } else { - AuctionState::Active(Box::new(active_auction.clone())) + current_state.clone() } } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index e95055b56..953e9a11e 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -44,7 +44,6 @@ use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; use crate::shimless::initialize::initialize_program; use crate::testing_engine::setup::ShimMode; -use crate::utils::auction::AuctionState; use crate::utils::token_account::SplTokenEnum; use crate::utils::vaa::TestVaaPairs; use crate::utils::{auction::AuctionAccounts, router::create_all_router_endpoints_test}; @@ -552,35 +551,13 @@ impl TestingEngine { current_state.router_endpoints().is_some(), "Router endpoints are not created" ); - - let initial_offer_placed_state = shimless::make_offer::place_initial_offer_shimless( + shimless::make_offer::place_initial_offer_shimless( &self.testing_context, test_context, current_state, config, ) - .await; - if config.expected_error().is_none() { - let initial_offer_placed_state = initial_offer_placed_state.unwrap(); - let auction_state = initial_offer_placed_state.auction_state; - let auction_accounts = initial_offer_placed_state.auction_accounts; - auction_state - .get_active_auction() - .unwrap() - .verify_auction(&self.testing_context, test_context) - .await - .expect("Could not verify auction state"); - return TestingEngineState::InitialOfferPlaced { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state, - auction_accounts, - order_prepared: current_state.order_prepared().cloned(), - }; - } - current_state.clone() + .await } /// Instruction trigger function for improving an offer @@ -590,34 +567,13 @@ impl TestingEngine { current_state: &TestingEngineState, config: &ImproveOfferInstructionConfig, ) -> TestingEngineState { - let expected_error = config.expected_error(); - let actor = config.actor.get_actor(&self.testing_context.testing_actors); - let payer_signer = config - .payer_signer - .clone() - .unwrap_or_else(|| self.testing_context.testing_actors.payer_signer.clone()); - let new_auction_state = shimless::make_offer::improve_offer( + shimless::make_offer::improve_offer( &self.testing_context, test_context, - &actor, + current_state, config, - &payer_signer, - current_state.auction_state(), - expected_error, ) - .await; - if expected_error.is_none() { - return TestingEngineState::OfferImproved { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: new_auction_state, - auction_accounts: current_state.auction_accounts().cloned(), - order_prepared: current_state.order_prepared().cloned(), - }; - } - current_state.clone() + .await } /// Instruction trigger function for placing an initial offer @@ -687,36 +643,14 @@ impl TestingEngine { current_state.spl_token_enum().unwrap(), current_state.base().transfer_direction, ); - let result = shimless::execute_order::execute_order_shimless_test( + shimless::execute_order::execute_order_shimless( &self.testing_context, test_context, + current_state, config, &auction_accounts, - current_state.auction_state(), - config.expected_error(), ) - .await; - if config.expected_error.is_none() { - let execute_order_fixture = result.unwrap(); - let order_executed_state = OrderExecutedState { - cctp_message: execute_order_fixture.cctp_message, - post_message_sequence: None, - post_message_message: None, - actor_enum: config.actor_enum, - }; - TestingEngineState::OrderExecuted { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_executed: order_executed_state, - auction_accounts: auction_accounts.clone(), - order_prepared: current_state.order_prepared().cloned(), - } - } else { - current_state.clone() - } + .await } /// Instruction trigger function for preparing an order @@ -742,44 +676,13 @@ impl TestingEngine { current_state: &TestingEngineState, config: &PrepareOrderResponseInstructionConfig, ) -> TestingEngineState { - let auction_accounts = current_state - .auction_accounts() - .expect("Auction accounts not found"); - let solver_token_account = config - .actor_enum - .get_actor(&self.testing_context.testing_actors) - .token_account_address(&config.token_enum) - .expect("Token account does not exist for solver at index"); - println!("Base fee token address: {:?}", solver_token_account); - let result = shimless::prepare_order_response::prepare_order_response( + shimless::prepare_order_response::prepare_order_response( &self.testing_context, test_context, config, current_state, - &solver_token_account, ) - .await; - if config.expected_error.is_none() { - let prepare_order_response_fixture = result.unwrap(); - let order_prepared_state = OrderPreparedState { - prepared_order_response_address: prepare_order_response_fixture - .prepared_order_response, - prepared_custody_token: prepare_order_response_fixture.prepared_custody_token, - base_fee_token: prepare_order_response_fixture.base_fee_token, - actor_enum: config.actor_enum, - }; - TestingEngineState::OrderPrepared { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - auction_state: current_state.auction_state().clone(), - order_prepared: order_prepared_state, - auction_accounts: auction_accounts.clone(), - } - } else { - current_state.clone() - } + .await } /// Instruction trigger function for settling an auction @@ -789,27 +692,14 @@ impl TestingEngine { current_state: &TestingEngineState, config: &SettleAuctionInstructionConfig, ) -> TestingEngineState { - let auction_state = shimless::settle_auction::settle_auction_complete( + shimless::settle_auction::settle_auction_complete( &self.testing_context, current_state, test_context, config, config.expected_error(), ) - .await; - match auction_state { - AuctionState::Settled => TestingEngineState::AuctionSettled { - base: current_state.base().clone(), - initialized: current_state.initialized().unwrap().clone(), - router_endpoints: current_state.router_endpoints().unwrap().clone(), - auction_state: current_state.auction_state().clone(), - fast_market_order: current_state.fast_market_order().cloned(), - order_prepared: current_state.order_prepared().unwrap().clone(), - auction_accounts: current_state.auction_accounts().cloned(), - order_executed: current_state.order_executed().cloned(), - }, - _ => current_state.clone(), - } + .await } // -------------------------------------------------------------------------------------------- diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index 0c5683e35..fc8cf4fd1 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -47,7 +47,7 @@ pub struct AuctionAccounts { pub enum AuctionState { Active(Box), Paused(Box), - Settled, + Settled(Box), Inactive, } @@ -57,7 +57,7 @@ impl AuctionState { AuctionState::Active(auction) => Some(auction), AuctionState::Paused(auction) => Some(auction), AuctionState::Inactive => None, - AuctionState::Settled => None, + AuctionState::Settled(auction) => Some(auction), } } From 70539c204ea0ae0d7c62ad943eb61b5f375caf05 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 9 May 2025 16:08:43 +0100 Subject: [PATCH 091/112] small docstring addition --- .../matching-engine-testing/tests/testing_engine/engine.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 953e9a11e..8484e9ff0 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -724,6 +724,7 @@ impl TestingEngine { current_state.clone() } + /// Verify the balances after an instruction has been executed. Currently only used for execute order instruction async fn verify_balances( &self, test_context: &mut ProgramTestContext, @@ -773,6 +774,7 @@ impl TestingEngine { // Combination trigger functions // -------------------------------------------------------------------------------------------- + /// A transaction that combines the instructions for creating a fast market order and placing an initial offer async fn create_fast_market_order_and_place_initial_offer( &self, test_context: &mut ProgramTestContext, From 6f736b5c61f5900d0201c63dc7fdd1854a48dc4d Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Fri, 9 May 2025 17:01:04 +0100 Subject: [PATCH 092/112] README updated to actually show how to run the tests --- .../matching-engine-testing/tests/README.md | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/solana/modules/matching-engine-testing/tests/README.md b/solana/modules/matching-engine-testing/tests/README.md index b59c22aa9..0f6ee8755 100644 --- a/solana/modules/matching-engine-testing/tests/README.md +++ b/solana/modules/matching-engine-testing/tests/README.md @@ -10,10 +10,36 @@ Each test is a function that is annotated with `#[tokio::test]`. Each test is a test for a specific scenario, and uses the `TestingEngine` to execute a series of instruction triggers. -The `TestingEngine` is initialized with a `TestingContext`. The `TestingContext` holds the solana program test context, the actors, the transfer direction, created vaas, as well as some constants. +The `TestingEngine` is initialized with a `TestingContext`. The `TestingContext` holds the the `TestingActors`, the transfer direction, created vaas, as well as some constants. + +The `TestingActors` are structs that hold information for any keypair that is setup before the tests are conducted. These include the `owner` the `owner_assistant` and the `Solvers`. The `TestingEngine` is used to execute the instruction triggers in the order they are provided. See the `testing_engine/engine.rs` file for more details. +## How to run the tests + +### Setup for running the tests + +The program must be built. This is done by entering the `solana/programs/matching-enginge` directory and running `cargo build-sbf --features mainnet`. With an incorrect `so` file, the tests will not be run against the correct program. + +```bash +cd solana/programs/matching-engine +cargo build-sbf --features mainnet +``` + +### Running the tests + +The tests are run by the following command + +```bash +cd solana/modules/matching-engine-testing +cargo test-sbf --features mainnet -- --show-output --test-threads 5 +``` + +#### ❗❗ NOTE when running tests +In order to run tests successfully and avoiding an annoying error due to an RpcTimeout, use a low number of `--test-threads`. This will depend on the local machine. The current recommended threads is `5`. + + ## Happy path integration tests ### Initialize program From 1a3dcf49d1fb27c219301a84dc076e45b471c316 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 15:17:15 -0500 Subject: [PATCH 093/112] solana: update Makefile --- solana/Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/solana/Makefile b/solana/Makefile index 60904f5d6..e2f70e70e 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -68,6 +68,15 @@ check-idl: idl $(BUILD_$(NETWORK)): cargo-test +.PHONY: test-sbf +test-sbf: +### Because the tests are performed in a separate module, we need to ensure that the programs are +### built prior to performing `cargo test-sbf`. + cargo build-sbf --features mainnet +### Unfortunately we cannot saturate all CPUs to perform tests due to nondeterministic `RpcError` +### reverts. We constrain the number of threads when we run these tests. + cd modules/matching-engine-testing && cargo test-sbf --features mainnet -- --test-threads 4 + .PHONY: anchor-test anchor-test: anchor-test-setup cp target/deploy/upgrade_manager.so ts/tests/artifacts/testnet_upgrade_manager.so From d6d9e7e9875d20242a2dbb6a5b3dd0e25531c348 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 15:17:59 -0500 Subject: [PATCH 094/112] solana: clean up helpers --- .../src/fallback/processor/helpers.rs | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index b9242c485..9d2c69660 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,21 +1,21 @@ use anchor_lang::prelude::*; - -use crate::ID; -use anchor_spl::mint::USDC; use anchor_spl::token::spl_token; -use solana_program::program_pack::Pack; use solana_program::{ entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, program::invoke_signed_unchecked, + program_pack::Pack, system_instruction, }; +use crate::ID; + #[inline(always)] -pub fn require_min_account_infos_len(accounts: &[AccountInfo], len: usize) -> Result<()> { - if accounts.len() < len { +pub fn require_min_account_infos_len(accounts: &[AccountInfo], at_least_len: usize) -> Result<()> { + if accounts.len() < at_least_len { return Err(ErrorCode::AccountNotEnoughKeys.into()); } + Ok(()) } @@ -140,31 +140,29 @@ pub fn create_account_reliably( Ok(()) } -/// Create a token account reliably +/// Create a USDC token account reliably. /// -/// This function creates a token account and initializes it with the given mint and owner. +/// This function creates a USDC token account and initializes it with the given owner. /// /// # Arguments /// -/// * `payer_pubkey` - The pubkey of the account that will pay for the token account. -/// * `account_pubkey_to_create` - The pubkey of the account to create. -/// * `owner_account_info` - The account info of the owner of the token account. -/// * `mint_pubkey` - The pubkey of the mint. -/// * `data_len` - The length of the data to be written to the token account. +/// * `payer_key` - The pubkey of the account that will pay for the token account. +/// * `token_account_key` - The pubkey of the account to create. +/// * `token_account_owner_key` - The account info of the owner of the token account. +/// * `token_account_lamports` - Current lamports on token account. /// * `accounts` - The accounts to be used in the CPI. /// * `signer_seeds` - The signer seeds to be used in the CPI. pub fn create_usdc_token_account_reliably( - payer_pubkey: &Pubkey, - account_pubkey_to_create: &Pubkey, - owner_account_pubkey: &Pubkey, + payer_key: &Pubkey, + token_account_key: &Pubkey, + token_account_owner_key: &Pubkey, token_account_lamports: u64, accounts: &[AccountInfo], signer_seeds: &[&[&[u8]]], ) -> ProgramResult { - // Create the owner account create_account_reliably( - payer_pubkey, - account_pubkey_to_create, + payer_key, + token_account_key, token_account_lamports, spl_token::state::Account::LEN, accounts, @@ -172,15 +170,13 @@ pub fn create_usdc_token_account_reliably( signer_seeds, )?; - // Create the token account let init_token_account_ix = spl_token::instruction::initialize_account3( &spl_token::ID, - account_pubkey_to_create, - &USDC, - owner_account_pubkey, - )?; + token_account_key, + &common::USDC_MINT, + token_account_owner_key, + ) + .unwrap(); - solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[])?; - - Ok(()) + solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[]) } From 3f7b0bc53e5d403daded2b234309e3f93de2fbaf Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 15:56:02 -0500 Subject: [PATCH 095/112] solana: clean up fast market order --- .../src/state/fast_market_order.rs | 94 +++++++++---------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 1b42fcf76..8f3d0441d 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -1,52 +1,57 @@ -use anchor_lang::prelude::*; -use anchor_lang::Discriminator; +use anchor_lang::{prelude::*, Discriminator}; use solana_program::keccak; -/// An account that represents a fast market order vaa. It is created by the signer of the transaction, and owned by the matching engine program. -/// The of the account is able to close this account and redeem the lamports deposited into the account (for rent) +/// An account that represents a fast market order VAA. It is created by the +/// payer of the transaction. This payer is the only authority that can close +/// this account and receive its rent. #[account(zero_copy)] #[derive(Debug)] #[repr(C)] pub struct FastMarketOrder { - /// The amount of tokens sent from the source chain via the fast transfer + /// The amount of tokens sent from the source chain via the fast transfer. pub amount_in: u64, - /// The minimum amount of tokens to be received on the target chain via the fast transfer + /// The minimum amount of tokens to be received on the target chain via the + /// fast transfer. pub min_amount_out: u64, - /// The deadline of the auction + /// The deadline of the auction. pub deadline: u32, - /// The target chain (represented as a wormhole chain id) + /// The target chain (represented as a Wormhole chain ID). pub target_chain: u16, - /// The length of the redeemer message + /// The length of the redeemer message. pub redeemer_message_length: u16, - /// The redeemer of the fast transfer (on the destination chain) + /// The redeemer of the fast transfer (on the destination chain). pub redeemer: [u8; 32], - /// The sender of the fast transfer (on the source chain) + /// The sender of the fast transfer (on the source chain). pub sender: [u8; 32], - /// The refund address of the fast transfer + /// The refund address of the fast transfer. pub refund_address: [u8; 32], - /// The maximum fee of the fast transfer + /// The maximum fee of the fast transfer. pub max_fee: u64, - /// The initial auction fee of the fast transfer + /// The initial auction fee of the fast transfer. pub init_auction_fee: u64, - /// The redeemer message of the fast transfer - /// NOTE: This value is based on the max redeemer length of 500 bytes that is specified in the token router program. If this changes in the future, this value must be updated. + /// The redeemer message of the fast transfer. + /// + /// NOTE: This value is based on the max redeemer length of 500 bytes that + /// is specified in the token router program. If this changes in the future, + /// this value must be updated. pub redeemer_message: [u8; 512], - /// The refund recipient for the creator of the fast market order account + /// The refund recipient for the creator of the fast market order account. pub close_account_refund_recipient: Pubkey, /// The emitter address of the fast transfer pub vaa_emitter_address: [u8; 32], - /// The sequence of the fast transfer vaa + /// The sequence of the fast transfer VAA. pub vaa_sequence: u64, - /// The timestamp of the fast transfer vaa + /// The timestamp of the fast transfer VAA. pub vaa_timestamp: u32, - /// The vaa nonce, which is not used and can be set to 0. + /// The VAA nonce, which is not used and can be set to 0. // TODO: Can be taken out. pub vaa_nonce: u32, - /// The source chain of the fast transfer vaa (represented as a wormhole chain id) + /// The source chain of the fast transfer VAA. (represented as a Wormhole + /// chain ID). pub vaa_emitter_chain: u16, - /// The consistency level of the fast transfer vaa + /// The consistency level of the fast transfer VAA. pub vaa_consistency_level: u8, - /// Not used, but required for bytemuck serialisation + /// Not used, but required for bytemuck serialization. _padding: [u8; 5], } @@ -72,6 +77,8 @@ pub struct FastMarketOrderParams { } impl FastMarketOrder { + pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; + pub fn new(params: FastMarketOrderParams) -> Self { Self { amount_in: params.amount_in, @@ -96,16 +103,6 @@ impl FastMarketOrder { } } - pub const SEED_PREFIX: &'static [u8] = b"fast_market_order"; - - /// Convert the fast market order to a vec of bytes (without the discriminator) - pub fn to_vec(&self) -> Vec { - let payload_slice = bytemuck::bytes_of(self); - let mut payload = Vec::with_capacity(payload_slice.len()); - payload.extend_from_slice(payload_slice); - payload - } - /// Creates an payload as expected in a fast market order vaa pub fn payload(&self) -> Vec { let mut payload = vec![]; @@ -128,22 +125,23 @@ impl FastMarketOrder { payload } - /// A double hash of the serialised fast market order. Used for seeds and verification. + /// A double hash of the serialised fast market order. Used for seeds and + /// verification. + /// TODO: Change return type to keccak::Hash pub fn digest(&self) -> [u8; 32] { - let message_hash = keccak::hashv(&[ - self.vaa_timestamp.to_be_bytes().as_ref(), - self.vaa_nonce.to_be_bytes().as_ref(), - self.vaa_emitter_chain.to_be_bytes().as_ref(), - &self.vaa_emitter_address, - &self.vaa_sequence.to_be_bytes(), - &[self.vaa_consistency_level], - self.payload().as_ref(), - ]); - // Digest is the hash of the message - keccak::hashv(&[message_hash.as_ref()]) - .as_ref() - .try_into() - .unwrap() + wormhole_svm_definitions::compute_keccak_digest( + keccak::hashv(&[ + &self.vaa_timestamp.to_be_bytes(), + &self.vaa_nonce.to_be_bytes(), + &self.vaa_emitter_chain.to_be_bytes(), + &self.vaa_emitter_address, + &self.vaa_sequence.to_be_bytes(), + &[self.vaa_consistency_level], + &self.payload(), + ]), + None, + ) + .0 } /// Read from an account info From 7765d027d0f1f11960790c1aa461c480d70ba762 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 15:57:53 -0500 Subject: [PATCH 096/112] solana: begin fallback processor clean up clean up initialize fast market order --- .../processor/initialize_fast_market_order.rs | 253 +++++++++--------- .../src/fallback/processor/mod.rs | 6 +- .../fallback/processor/process_instruction.rs | 83 +++--- 3 files changed, 174 insertions(+), 168 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index 260231906..92bab344a 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -1,84 +1,76 @@ -use anchor_lang::prelude::*; -use anchor_lang::Discriminator; -use anchor_spl::token_interface::spl_token_metadata_interface::borsh::BorshDeserialize; +use anchor_lang::{prelude::*, Discriminator}; use bytemuck::{Pod, Zeroable}; -use solana_program::instruction::Instruction; -use solana_program::keccak; -use solana_program::program::invoke_signed_unchecked; -use wormhole_svm_shim::verify_vaa::VerifyHash; -use wormhole_svm_shim::verify_vaa::VerifyHashAccounts; -use wormhole_svm_shim::verify_vaa::VerifyHashData; +use solana_program::{instruction::Instruction, keccak, program::invoke_signed_unchecked}; +use wormhole_svm_shim::verify_vaa; -use super::helpers::create_account_reliably; - -use super::helpers::require_min_account_infos_len; -use super::FallbackMatchingEngineInstruction; -use crate::error::MatchingEngineError; -use crate::state::FastMarketOrder as FastMarketOrderState; -use crate::ID; +use crate::{error::MatchingEngineError, state::FastMarketOrder, ID}; pub struct InitializeFastMarketOrderAccounts<'ix> { - /// The signer of the transaction + /// Lamports from this signer will be used to create the new fast market + /// order account. This account will be the only authority allowed to + /// close this account. + /// TODO: Rename to "payer". pub signer: &'ix Pubkey, - /// The fast market order account pubkey (that is created by the instruction) + /// The fast market order account pubkey (that is created by the + /// instruction). + /// TODO: Rename to "new_fast_market_order". pub fast_market_order_account: &'ix Pubkey, - /// The guardian set account pubkey + /// Wormhole guardian set account used to check recovered pubkeys using + /// [Self::guardian_set_signatures]. + /// TODO: Rename to "wormhole_guardian_set" pub guardian_set: &'ix Pubkey, - /// The guardian set signatures account pubkey (created by the post verify vaa shim program) + /// The guardian set signatures of fast market order VAA. + /// TODO: Rename to "shim_guardian_signatures". pub guardian_set_signatures: &'ix Pubkey, - /// The verify vaa shim program pubkey pub verify_vaa_shim_program: &'ix Pubkey, - /// The system program account pubkey + /// TODO: Remove. pub system_program: &'ix Pubkey, } -impl<'ix> InitializeFastMarketOrderAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new(*self.signer, true), // This will be the refund recipient - AccountMeta::new(*self.fast_market_order_account, false), - AccountMeta::new_readonly(*self.guardian_set, false), - AccountMeta::new_readonly(*self.guardian_set_signatures, false), - AccountMeta::new_readonly(*self.verify_vaa_shim_program, false), - AccountMeta::new_readonly(*self.system_program, false), - ] - } -} - #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct InitializeFastMarketOrderData { /// The fast market order as the bytemuck struct - pub fast_market_order: FastMarketOrderState, + pub fast_market_order: FastMarketOrder, /// The guardian set bump pub guardian_set_bump: u8, /// Padding to ensure bytemuck deserialization works _padding: [u8; 7], } + impl InitializeFastMarketOrderData { // Adds the padding to the InitializeFastMarketOrderData - pub fn new(fast_market_order: FastMarketOrderState, guardian_set_bump: u8) -> Self { + pub fn new(fast_market_order: FastMarketOrder, guardian_set_bump: u8) -> Self { Self { fast_market_order, guardian_set_bump, - _padding: [0_u8; 7], + _padding: Default::default(), } } - /// Deserializes the InitializeFastMarketOrderData from a byte slice + /// Deserializes the InitializeFastMarketOrderData from a byte slice. /// /// # Arguments /// - /// * `data` - A byte slice containing the InitializeFastMarketOrderData + /// * `data` - A byte slice containing the InitializeFastMarketOrderData. /// /// # Returns /// - /// Option<&Self> - The deserialized InitializeFastMarketOrderData or None if the byte slice is not the correct length + /// Option<&Self> - The deserialized `InitializeFastMarketOrderData`` or + /// `None` if the byte slice is not the correct length. pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() + bytemuck::try_from_bytes(data).ok() } } +/// Initializes the fast market order account. +/// +/// The verify shim program first checks that the digest of the fast market +/// order is correct, and that the guardian signature is correct and +/// recoverable. If this is the case, the fast market order account is created. +/// The fast market order account is owned by the matching engine program. It +/// can be closed by the close fast market order instruction, which returns the +/// lamports to the close account refund recipient. pub struct InitializeFastMarketOrder<'ix> { pub program_id: &'ix Pubkey, pub accounts: InitializeFastMarketOrderAccounts<'ix>, @@ -87,113 +79,120 @@ pub struct InitializeFastMarketOrder<'ix> { impl InitializeFastMarketOrder<'_> { pub fn instruction(&self) -> Instruction { + let InitializeFastMarketOrderAccounts { + signer: payer, + fast_market_order_account: new_fast_market_order, + guardian_set: wormhole_guardian_set, + guardian_set_signatures: shim_guardian_signatures, + verify_vaa_shim_program, + system_program: _, + } = self.accounts; + Instruction { program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::InitializeFastMarketOrder(&self.data).to_vec(), + accounts: vec![ + AccountMeta::new(*payer, true), + AccountMeta::new(*new_fast_market_order, false), + AccountMeta::new_readonly(*wormhole_guardian_set, false), + AccountMeta::new_readonly(*shim_guardian_signatures, false), + AccountMeta::new_readonly(*verify_vaa_shim_program, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + ], + data: super::FallbackMatchingEngineInstruction::InitializeFastMarketOrder(&self.data) + .to_vec(), } } } -/// Initializes the fast market order account -/// -/// The verify shim program first checks that the digest of the fast market order is correct, and that the guardian signature is correct and recoverable. -/// If this is the case, the fast market order account is created. The fast market order account is owned by the matching engine program. It can be closed -/// by the close fast market order instruction, which returns the lamports to the close account refund recipient. -/// -/// # Arguments -/// -/// * `accounts` - The accounts of the fast market order and the guardian set -/// -/// # Returns -/// -/// Result<()> -pub fn initialize_fast_market_order( +pub(super) fn process( accounts: &[AccountInfo], data: &InitializeFastMarketOrderData, ) -> Result<()> { - require_min_account_infos_len(accounts, 6)?; - - let signer = &accounts[0]; - let fast_market_order_account = &accounts[1]; - let guardian_set = &accounts[2]; - let guardian_set_signatures = &accounts[3]; - - let InitializeFastMarketOrderData { - fast_market_order, - guardian_set_bump, - _padding: _, - } = data; - // Start of cpi call to verify the shim. - // ------------------------------------------------------------------------------------------------ + super::helpers::require_min_account_infos_len(accounts, 6)?; + + let fast_market_order = &data.fast_market_order; + + // Generate the VAA digest, which will be used to verify the guardian + // signatures. let fast_market_order_vaa_digest = fast_market_order.digest(); - let fast_market_order_vaa_digest_hash = - keccak::Hash::try_from_slice(&fast_market_order_vaa_digest).unwrap(); - let verify_hash_data = - VerifyHashData::new(*guardian_set_bump, fast_market_order_vaa_digest_hash); - let verify_hash_shim_ix = VerifyHash { - program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: VerifyHashAccounts { - guardian_set: &guardian_set.key(), - guardian_signatures: &guardian_set_signatures.key(), - }, - data: verify_hash_data, - } - .instruction(); - // Make the cpi call to verify the shim. - invoke_signed_unchecked(&verify_hash_shim_ix, accounts, &[])?; - // ------------------------------------------------------------------------------------------------ - // End of cpi call to verify the shim. - - // Start of fast market order account creation - // ------------------------------------------------------------------------------------------------ - let fast_market_order_key = fast_market_order_account.key(); - let space = 8_usize.saturating_add(std::mem::size_of::()); - let (fast_market_order_pda, fast_market_order_bump) = Pubkey::find_program_address( + + // This payer will send lamports to the new fast market order account and + // will be the "owner" of this account. Only this account can close the + // fast market order account. + let payer_info = &accounts[0]; + + // Verify that the fast market order account's key is derived correctly. + let new_fast_market_order_info = &accounts[1]; + let fast_market_order_key = new_fast_market_order_info.key; + let (expected_fast_market_order_key, fast_market_order_bump) = Pubkey::find_program_address( &[ - FastMarketOrderState::SEED_PREFIX, - fast_market_order_vaa_digest.as_ref(), + FastMarketOrder::SEED_PREFIX, + &fast_market_order_vaa_digest, fast_market_order.close_account_refund_recipient.as_ref(), ], &ID, ); - if fast_market_order_pda != fast_market_order_key { - msg!("Fast market order pda is invalid"); - return Err(MatchingEngineError::InvalidPda.into()) - .map_err(|e: Error| e.with_pubkeys((fast_market_order_key, fast_market_order_pda))); + if fast_market_order_key != &expected_fast_market_order_key { + return Err(MatchingEngineError::InvalidPda.into()).map_err(|e: Error| { + e.with_account_name("fast_market_order") + .with_pubkeys((*fast_market_order_key, expected_fast_market_order_key)) + }); } - let fast_market_order_seeds = [ - FastMarketOrderState::SEED_PREFIX, - fast_market_order_vaa_digest.as_ref(), - fast_market_order.close_account_refund_recipient.as_ref(), - &[fast_market_order_bump], - ]; - let fast_market_order_signer_seeds = &[&fast_market_order_seeds[..]]; - // Create the account using the system program. The create account reliably ensures that the account creation cannot be raced. - create_account_reliably( - &signer.key(), - &fast_market_order_key, - fast_market_order_account.lamports(), - space, + + // These accounts will be used by the Verify VAA shim program. + let wormhole_guardian_set_info = &accounts[2]; + let shim_guardian_signatures_info = &accounts[3]; + + // Verify the VAA digest with the Verify VAA shim program. + invoke_signed_unchecked( + &verify_vaa::VerifyHash { + program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: verify_vaa::VerifyHashAccounts { + guardian_set: wormhole_guardian_set_info.key, + guardian_signatures: shim_guardian_signatures_info.key, + }, + data: verify_vaa::VerifyHashData::new( + data.guardian_set_bump, + keccak::Hash(fast_market_order_vaa_digest), + ), + } + .instruction(), accounts, - &ID, - fast_market_order_signer_seeds, + &[], )?; - // Borrow the account data mutably - let mut fast_market_order_account_data = fast_market_order_account.try_borrow_mut_data()?; - // Write the discriminator to the first 8 bytes - let discriminator = FastMarketOrderState::discriminator(); - fast_market_order_account_data[0..8].copy_from_slice(&discriminator); + // Create the new fast market order account and serialize the instruction + // data into it. + + const DISCRIMINATOR_LEN: usize = FastMarketOrder::DISCRIMINATOR.len(); + const FAST_MARKET_ORDER_DATA_LEN: usize = + DISCRIMINATOR_LEN + std::mem::size_of::(); + + super::helpers::create_account_reliably( + payer_info.key, + fast_market_order_key, + new_fast_market_order_info.lamports(), + FAST_MARKET_ORDER_DATA_LEN, + accounts, + &ID, + &[&[ + FastMarketOrder::SEED_PREFIX, + &fast_market_order_vaa_digest, + // TODO: Replace with payer_info.key. + fast_market_order.close_account_refund_recipient.as_ref(), + &[fast_market_order_bump], + ]], + )?; - let fast_market_order_bytes = bytemuck::bytes_of(fast_market_order); + let mut new_fast_market_order_info_data = new_fast_market_order_info.try_borrow_mut_data()?; - // Write the fast_market_order struct to the account - fast_market_order_account_data[8..8_usize.saturating_add(fast_market_order_bytes.len())] - .copy_from_slice(fast_market_order_bytes); - // End of fast market order account creation - // ------------------------------------------------------------------------------------------------ + // Write provided fast market order data to account starting with its + // discriminator. + new_fast_market_order_info_data[0..DISCRIMINATOR_LEN] + .copy_from_slice(&FastMarketOrder::DISCRIMINATOR); + new_fast_market_order_info_data[DISCRIMINATOR_LEN..FAST_MARKET_ORDER_DATA_LEN] + .copy_from_slice(bytemuck::bytes_of(fast_market_order)); Ok(()) } diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 6fcba8152..47b788f8e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,10 +1,10 @@ -pub mod process_instruction; -pub use process_instruction::*; pub mod burn_and_post; pub mod close_fast_market_order; pub mod execute_order; +pub mod helpers; pub mod initialize_fast_market_order; pub mod place_initial_offer; pub mod prepare_order_response; +pub mod process_instruction; -pub mod helpers; +pub use process_instruction::*; diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 7e88c765b..3d522e968 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -1,25 +1,28 @@ +use anchor_lang::prelude::*; +use wormhole_svm_definitions::make_anchor_discriminator; + +use crate::ID; + use super::close_fast_market_order::close_fast_market_order; use super::execute_order::handle_execute_order_shim; -use super::initialize_fast_market_order::{ - initialize_fast_market_order, InitializeFastMarketOrderData, -}; +use super::initialize_fast_market_order::{self, InitializeFastMarketOrderData}; use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; -use super::prepare_order_response::prepare_order_response_cctp_shim; -use super::prepare_order_response::PrepareOrderResponseCctpShimData; -use crate::ID; -use anchor_lang::prelude::*; -use wormhole_svm_definitions::make_anchor_discriminator; +use super::prepare_order_response::{ + prepare_order_response_cctp_shim, PrepareOrderResponseCctpShimData, +}; + +const SELECTOR_SIZE: usize = 8; impl<'ix> FallbackMatchingEngineInstruction<'ix> { - pub const INITIALIZE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + pub const INITIALIZE_FAST_MARKET_ORDER_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:initialize_fast_market_order"); - pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; 8] = + pub const CLOSE_FAST_MARKET_ORDER_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:close_fast_market_order"); - pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; 8] = + pub const PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:place_initial_offer_cctp_shim"); - pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; 8] = + pub const EXECUTE_ORDER_CCTP_SHIM_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:execute_order_cctp_shim"); - pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; 8] = + pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); } @@ -40,10 +43,12 @@ pub fn process_instruction( return Err(ErrorCode::InvalidProgramId.into()); } - let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data).unwrap(); + let instruction = FallbackMatchingEngineInstruction::deserialize(instruction_data) + .ok_or_else(|| ErrorCode::InstructionDidNotDeserialize)?; + match instruction { FallbackMatchingEngineInstruction::InitializeFastMarketOrder(data) => { - initialize_fast_market_order(accounts, data) + initialize_fast_market_order::process(accounts, data) } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { close_fast_market_order(accounts) @@ -62,20 +67,22 @@ pub fn process_instruction( impl<'ix> FallbackMatchingEngineInstruction<'ix> { pub fn deserialize(instruction_data: &'ix [u8]) -> Option { - if instruction_data.len() < 8 { + if instruction_data.len() < SELECTOR_SIZE { return None; } - match instruction_data[..8].try_into().unwrap() { + match instruction_data[..SELECTOR_SIZE].try_into().unwrap() { FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { Some(Self::PlaceInitialOfferCctpShim( - PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[SELECTOR_SIZE..]) + .unwrap(), )) } - - FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR => Some( - Self::InitializeFastMarketOrder(bytemuck::from_bytes(&instruction_data[8..])), - ), + FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR => { + Some(Self::InitializeFastMarketOrder(bytemuck::from_bytes( + &instruction_data[SELECTOR_SIZE..], + ))) + } FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) } @@ -84,7 +91,10 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { } FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { Some(Self::PrepareOrderResponseCctpShim( - PrepareOrderResponseCctpShimData::from_bytes(&instruction_data[8..]).unwrap(), + PrepareOrderResponseCctpShimData::from_bytes( + &instruction_data[SELECTOR_SIZE..], + ) + .unwrap(), )) } _ => None, @@ -95,6 +105,18 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { impl FallbackMatchingEngineInstruction<'_> { pub fn to_vec(&self) -> Vec { match self { + Self::InitializeFastMarketOrder(data) => { + let mut out = Vec::with_capacity( + std::mem::size_of::().saturating_add(8), + ); + + out.extend_from_slice( + &FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR, + ); + out.extend_from_slice(bytemuck::bytes_of(*data)); + + out + } Self::PlaceInitialOfferCctpShim(data) => { // Calculate the total capacity needed let data_slice = bytemuck::bytes_of(*data); @@ -107,8 +129,6 @@ impl FallbackMatchingEngineInstruction<'_> { out.extend_from_slice( &FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR, ); - - // Extend the vector with the data slice out.extend_from_slice(data_slice); out @@ -124,19 +144,6 @@ impl FallbackMatchingEngineInstruction<'_> { out } - Self::InitializeFastMarketOrder(data) => { - let data_slice = bytemuck::bytes_of(*data); - let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length - - let mut out = Vec::with_capacity(total_capacity); - - out.extend_from_slice( - &FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR, - ); - out.extend_from_slice(data_slice); - - out - } Self::CloseFastMarketOrder => { let total_capacity = 8; // 8 for the selector (no data) From 78b3e4b12d57d817ea55c7ba3459df39ae9090d4 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 16:23:56 -0500 Subject: [PATCH 097/112] solana: clean up close fast market order --- .../processor/close_fast_market_order.rs | 105 ++++++++---------- 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 5e50becd4..aba80d008 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -1,29 +1,19 @@ -use crate::error::MatchingEngineError; -use crate::state::FastMarketOrder; use anchor_lang::prelude::*; use solana_program::instruction::Instruction; -use solana_program::program_error::ProgramError; -use super::helpers::require_min_account_infos_len; -use super::FallbackMatchingEngineInstruction; +use crate::{error::MatchingEngineError, state::FastMarketOrder}; pub struct CloseFastMarketOrderAccounts<'ix> { - /// The fast market order account created from the initialize fast market order instruction + /// The fast market order account to be closed. pub fast_market_order: &'ix Pubkey, - /// The account that will receive the refund. CHECK: Must be a signer. - /// CHECK: Must match the close account refund recipient in the fast market order account + /// The account that will receive rent from the fast market order account. + /// This account is the only authority that can close the fast market order. + /// TODO: Rename to "refund_recipient". pub close_account_refund_recipient: &'ix Pubkey, } -impl<'ix> CloseFastMarketOrderAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new(*self.fast_market_order, false), - AccountMeta::new(*self.close_account_refund_recipient, true), - ] - } -} - +/// Closes the fast market order and transfers the lamports from the fast market +/// order to its refund recipient. pub struct CloseFastMarketOrder<'ix> { pub program_id: &'ix Pubkey, pub accounts: CloseFastMarketOrderAccounts<'ix>, @@ -31,64 +21,61 @@ pub struct CloseFastMarketOrder<'ix> { impl CloseFastMarketOrder<'_> { pub fn instruction(&self) -> Instruction { + let CloseFastMarketOrderAccounts { + fast_market_order, + close_account_refund_recipient: refund_recipient, + } = self.accounts; + Instruction { program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::CloseFastMarketOrder.to_vec(), + accounts: vec![ + AccountMeta::new(*fast_market_order, false), + AccountMeta::new(*refund_recipient, true), + ], + data: super::FallbackMatchingEngineInstruction::CloseFastMarketOrder.to_vec(), } } } -/// Closes the fast market order and transfers the lamports from the fast market order to the close account refund recipient -/// -/// # Arguments -/// -/// * `accounts` - The accounts of the fast market order and the close account refund recipient -/// -/// # Returns -/// -/// Result<()> pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { - require_min_account_infos_len(accounts, 2)?; + super::helpers::require_min_account_infos_len(accounts, 2)?; - let fast_market_order = &accounts[0]; - let close_account_refund_recipient = &accounts[1]; + // We need to check the refund recipient account against what we know as the + // refund recipient encoded in the fast market order account. + let fast_market_order_info = &accounts[0]; + let refund_recipient_info = &accounts[1]; - // Check that the close_account_refund_recipient is a signer, otherwise someone might call this and steal the lamports - if !close_account_refund_recipient.is_signer { - msg!("Refund recipient (account #2) is not a signer"); - return Err(ProgramError::InvalidAccountData.into()); - } - let fast_market_order_data = &fast_market_order.data.borrow()[..]; - let fast_market_order_deserialized = FastMarketOrder::try_read(fast_market_order_data)?; - // Check that the fast_market_order is owned by the close_account_refund_recipient - if fast_market_order_deserialized.close_account_refund_recipient - != close_account_refund_recipient.key() - { + let fast_market_order_data = &fast_market_order_info.data.borrow()[..]; + + // NOTE: We do not need to verify that the owner of this account is this + // program because the lamport transfer will fail otherwise. + let fast_market_order = FastMarketOrder::try_read(fast_market_order_data)?; + + // Check that the refund recipient provided in this instruction is the one + // encoded in the fast market order account. + let expected_refund_recipient_key = fast_market_order.close_account_refund_recipient; + if refund_recipient_info.key != &expected_refund_recipient_key { return Err(MatchingEngineError::MismatchingCloseAccountRefundRecipient.into()).map_err( - |e: Error| { - e.with_pubkeys(( - fast_market_order_deserialized.close_account_refund_recipient, - close_account_refund_recipient.key(), - )) - }, + |e: Error| e.with_pubkeys((*refund_recipient_info.key, expected_refund_recipient_key)), ); } - // First, get the current lamports value - let current_recipient_lamports = **close_account_refund_recipient.lamports.borrow(); - - // Then, get the fast market order lamports - let mut fast_market_order_lamports = fast_market_order.lamports.borrow_mut(); + // This refund recipient must sign to invoke this instruction. He is the + // only authority allowed to perform this action. + if !refund_recipient_info.is_signer { + return Err(ErrorCode::AccountNotSigner.into()) + .map_err(|e: Error| e.with_account_name("refund_recipient")); + } - // Calculate the new amount - let new_amount = current_recipient_lamports.saturating_add(**fast_market_order_lamports); + let mut fast_market_order_info_lamports = fast_market_order_info.lamports.borrow_mut(); - // Now update the recipient's lamports - **close_account_refund_recipient.lamports.borrow_mut() = new_amount; + // Move lamports to the refund recipient. + let mut recipient_info_lamports = refund_recipient_info.lamports.borrow_mut(); + **recipient_info_lamports = + recipient_info_lamports.saturating_add(**fast_market_order_info_lamports); - // Zero out the fast market order lamports - **fast_market_order_lamports = 0; + // Zero out the fast market order lamports. + **fast_market_order_info_lamports = 0; Ok(()) } From 6d334376406b284414b398833d7ac0ad4187ddb9 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 16:45:14 -0500 Subject: [PATCH 098/112] solana: more close fast market order fixes --- .../src/fallback/processor/close_fast_market_order.rs | 2 +- solana/programs/matching-engine/src/state/fast_market_order.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index aba80d008..8be4f318f 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -8,7 +8,7 @@ pub struct CloseFastMarketOrderAccounts<'ix> { pub fast_market_order: &'ix Pubkey, /// The account that will receive rent from the fast market order account. /// This account is the only authority that can close the fast market order. - /// TODO: Rename to "refund_recipient". + // TODO: Rename to "refund_recipient". pub close_account_refund_recipient: &'ix Pubkey, } diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 8f3d0441d..8a3455d41 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -127,7 +127,7 @@ impl FastMarketOrder { /// A double hash of the serialised fast market order. Used for seeds and /// verification. - /// TODO: Change return type to keccak::Hash + // TODO: Change return type to keccak::Hash pub fn digest(&self) -> [u8; 32] { wormhole_svm_definitions::compute_keccak_digest( keccak::hashv(&[ From 3ce3e3c4b8bbdaf3b0e7870bfb2dc6821b03d5e8 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 16:45:58 -0500 Subject: [PATCH 099/112] solana: more initialize fast market order fixes --- .../processor/initialize_fast_market_order.rs | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index 92bab344a..aaab935aa 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -9,21 +9,21 @@ pub struct InitializeFastMarketOrderAccounts<'ix> { /// Lamports from this signer will be used to create the new fast market /// order account. This account will be the only authority allowed to /// close this account. - /// TODO: Rename to "payer". + // TODO: Rename to "payer". pub signer: &'ix Pubkey, /// The fast market order account pubkey (that is created by the /// instruction). - /// TODO: Rename to "new_fast_market_order". + // TODO: Rename to "new_fast_market_order". pub fast_market_order_account: &'ix Pubkey, /// Wormhole guardian set account used to check recovered pubkeys using /// [Self::guardian_set_signatures]. - /// TODO: Rename to "wormhole_guardian_set" + // TODO: Rename to "wormhole_guardian_set" pub guardian_set: &'ix Pubkey, /// The guardian set signatures of fast market order VAA. - /// TODO: Rename to "shim_guardian_signatures". + // TODO: Rename to "shim_guardian_signatures". pub guardian_set_signatures: &'ix Pubkey, pub verify_vaa_shim_program: &'ix Pubkey, - /// TODO: Remove. + // TODO: Remove. pub system_program: &'ix Pubkey, } @@ -47,20 +47,6 @@ impl InitializeFastMarketOrderData { _padding: Default::default(), } } - - /// Deserializes the InitializeFastMarketOrderData from a byte slice. - /// - /// # Arguments - /// - /// * `data` - A byte slice containing the InitializeFastMarketOrderData. - /// - /// # Returns - /// - /// Option<&Self> - The deserialized `InitializeFastMarketOrderData`` or - /// `None` if the byte slice is not the correct length. - pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes(data).ok() - } } /// Initializes the fast market order account. From b447570bf51e4aa3df6391b192b69f9071484fcc Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 8 May 2025 17:26:55 -0500 Subject: [PATCH 100/112] solana: rename close fast market order processor fn clean up instruction serde --- .../processor/close_fast_market_order.rs | 2 +- .../fallback/processor/process_instruction.rs | 63 +++++++------------ 2 files changed, 22 insertions(+), 43 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 8be4f318f..d50d1b473 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -37,7 +37,7 @@ impl CloseFastMarketOrder<'_> { } } -pub fn close_fast_market_order(accounts: &[AccountInfo]) -> Result<()> { +pub fn process(accounts: &[AccountInfo]) -> Result<()> { super::helpers::require_min_account_infos_len(accounts, 2)?; // We need to check the refund recipient account against what we know as the diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 3d522e968..2670003cd 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -3,9 +3,8 @@ use wormhole_svm_definitions::make_anchor_discriminator; use crate::ID; -use super::close_fast_market_order::close_fast_market_order; use super::execute_order::handle_execute_order_shim; -use super::initialize_fast_market_order::{self, InitializeFastMarketOrderData}; +use super::initialize_fast_market_order::InitializeFastMarketOrderData; use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; use super::prepare_order_response::{ prepare_order_response_cctp_shim, PrepareOrderResponseCctpShimData, @@ -29,6 +28,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { pub enum FallbackMatchingEngineInstruction<'ix> { InitializeFastMarketOrder(&'ix InitializeFastMarketOrderData), CloseFastMarketOrder, + // TODO: Replace with u64. PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), @@ -48,10 +48,10 @@ pub fn process_instruction( match instruction { FallbackMatchingEngineInstruction::InitializeFastMarketOrder(data) => { - initialize_fast_market_order::process(accounts, data) + super::initialize_fast_market_order::process(accounts, data) } FallbackMatchingEngineInstruction::CloseFastMarketOrder => { - close_fast_market_order(accounts) + super::close_fast_market_order::process(accounts) } FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { place_initial_offer_cctp_shim(accounts, data) @@ -73,15 +73,14 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { match instruction_data[..SELECTOR_SIZE].try_into().unwrap() { FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR => { - Some(Self::PlaceInitialOfferCctpShim( - PlaceInitialOfferCctpShimData::from_bytes(&instruction_data[SELECTOR_SIZE..]) - .unwrap(), - )) + bytemuck::try_from_bytes(&instruction_data[SELECTOR_SIZE..]) + .ok() + .map(Self::PlaceInitialOfferCctpShim) } FallbackMatchingEngineInstruction::INITIALIZE_FAST_MARKET_ORDER_SELECTOR => { - Some(Self::InitializeFastMarketOrder(bytemuck::from_bytes( - &instruction_data[SELECTOR_SIZE..], - ))) + bytemuck::try_from_bytes(&instruction_data[SELECTOR_SIZE..]) + .ok() + .map(Self::InitializeFastMarketOrder) } FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR => { Some(Self::CloseFastMarketOrder) @@ -90,6 +89,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { Some(Self::ExecuteOrderCctpShim) } FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { + // TODO: Fix this Some(Self::PrepareOrderResponseCctpShim( PrepareOrderResponseCctpShimData::from_bytes( &instruction_data[SELECTOR_SIZE..], @@ -107,7 +107,7 @@ impl FallbackMatchingEngineInstruction<'_> { match self { Self::InitializeFastMarketOrder(data) => { let mut out = Vec::with_capacity( - std::mem::size_of::().saturating_add(8), + SELECTOR_SIZE + std::mem::size_of::(), ); out.extend_from_slice( @@ -118,53 +118,32 @@ impl FallbackMatchingEngineInstruction<'_> { out } Self::PlaceInitialOfferCctpShim(data) => { - // Calculate the total capacity needed - let data_slice = bytemuck::bytes_of(*data); - let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length - - // Create a vector with the calculated capacity - let mut out = Vec::with_capacity(total_capacity); + let mut out = Vec::with_capacity(SELECTOR_SIZE + std::mem::size_of::()); - // Add the selector out.extend_from_slice( &FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR, ); - out.extend_from_slice(data_slice); + out.extend_from_slice(bytemuck::bytes_of(*data)); out } Self::ExecuteOrderCctpShim => { - let total_capacity = 8; // 8 for the selector (no data) - - let mut out = Vec::with_capacity(total_capacity); - - out.extend_from_slice( - &FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR, - ); - - out + FallbackMatchingEngineInstruction::EXECUTE_ORDER_CCTP_SHIM_SELECTOR.to_vec() } Self::CloseFastMarketOrder => { - let total_capacity = 8; // 8 for the selector (no data) - - let mut out = Vec::with_capacity(total_capacity); - - out.extend_from_slice( - &FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR, - ); - - out + FallbackMatchingEngineInstruction::CLOSE_FAST_MARKET_ORDER_SELECTOR.to_vec() } Self::PrepareOrderResponseCctpShim(data) => { - let data_slice = data.try_to_vec().unwrap(); - let total_capacity = 8_usize.saturating_add(data_slice.len()); // 8 for the selector, plus the data length + // Use a temporary vector, which will be consumed by the output vector when it is + // extended. + let tmp_data = data.try_to_vec().unwrap(); - let mut out = Vec::with_capacity(total_capacity); + let mut out = Vec::with_capacity(tmp_data.len().saturating_add(SELECTOR_SIZE)); out.extend_from_slice( &FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR, ); - out.extend_from_slice(&data_slice); + out.extend(tmp_data); out } From dedf1ae52f14f830484ffe9cc6118348a6865715 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Fri, 9 May 2025 14:28:47 -0500 Subject: [PATCH 101/112] solana: clean up place initial offer update helpers and fix existing instructions --- .../tests/test_scenarios/make_offer.rs | 2 +- .../processor/close_fast_market_order.rs | 10 +- .../src/fallback/processor/execute_order.rs | 10 +- .../src/fallback/processor/helpers.rs | 192 +++++- .../src/fallback/processor/mod.rs | 2 + .../fallback/processor/place_initial_offer.rs | 592 +++++++----------- .../processor/prepare_order_response.rs | 25 +- .../fallback/processor/process_instruction.rs | 4 +- .../src/state/fast_market_order.rs | 16 +- 9 files changed, 422 insertions(+), 431 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs index 0cb2d1f61..244966d1f 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/make_offer.rs @@ -403,7 +403,7 @@ pub async fn test_place_initial_offer_fails_if_fast_market_order_not_created() { fast_market_order_address: OverwriteCurrentState::Some(fake_fast_market_order_address), expected_error: Some(ExpectedError { instruction_index: 2, - error_code: u32::from(ErrorCode::ConstraintOwner), + error_code: u32::from(ErrorCode::AccountDiscriminatorMismatch), // TODO: Revisit? error_string: "Fast market order account owner is invalid".to_string(), }), ..PlaceInitialOfferInstructionConfig::default() diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index d50d1b473..4ad029d45 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use solana_program::instruction::Instruction; -use crate::{error::MatchingEngineError, state::FastMarketOrder}; +use crate::error::MatchingEngineError; pub struct CloseFastMarketOrderAccounts<'ix> { /// The fast market order account to be closed. @@ -43,13 +43,9 @@ pub fn process(accounts: &[AccountInfo]) -> Result<()> { // We need to check the refund recipient account against what we know as the // refund recipient encoded in the fast market order account. let fast_market_order_info = &accounts[0]; - let refund_recipient_info = &accounts[1]; - - let fast_market_order_data = &fast_market_order_info.data.borrow()[..]; + let fast_market_order = super::helpers::try_fast_market_order_account(fast_market_order_info)?; - // NOTE: We do not need to verify that the owner of this account is this - // program because the lamport transfer will fail otherwise. - let fast_market_order = FastMarketOrder::try_read(fast_market_order_data)?; + let refund_recipient_info = &accounts[1]; // Check that the refund recipient provided in this instruction is the one // encoded in the fast market order account. diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index d6f83fcbb..2ef920307 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -195,8 +195,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // Do checks // ------------------------------------------------------------------------------------------------ - let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; - let fast_market_order_zero_copy = FastMarketOrderState::try_read(fast_market_order_data)?; + let fast_market_order_zero_copy = + super::helpers::try_fast_market_order_account(fast_market_order_account)?; + // Bind value for compiler (needed for pda seeds) let active_auction_key = active_auction_account.key(); @@ -222,7 +223,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { }; // Check custodian owner - check_custodian_owner_is_program_id(custodian_account)?; + super::helpers::require_owned_by_this_program(custodian_account, "custodian")?; // Check custodian deserialises into a checked custodian account let _checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; @@ -471,7 +472,6 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .saturating_add(active_auction_info.security_deposit) .saturating_sub(user_reward); - let penalized = penalty > 0; if penalized && active_auction_best_offer_token_account.key() != executor_token_account.key() { @@ -503,7 +503,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { init_auction_fee, ) .unwrap(); - + invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?; // Because the initial offer token was paid this fee, we account for it here. remaining_custodied_amount = diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 9d2c69660..c7437920e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -1,14 +1,21 @@ -use anchor_lang::prelude::*; +use std::cell::Ref; + +use anchor_lang::{prelude::*, Discriminator}; use anchor_spl::token::spl_token; use solana_program::{ entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, + keccak, program::invoke_signed_unchecked, program_pack::Pack, system_instruction, }; -use crate::ID; +use crate::{ + error::MatchingEngineError, + state::{AuctionConfig, Custodian, FastMarketOrder, MessageProtocol, RouterEndpoint}, + ID, +}; #[inline(always)] pub fn require_min_account_infos_len(accounts: &[AccountInfo], at_least_len: usize) -> Result<()> { @@ -20,11 +27,119 @@ pub fn require_min_account_infos_len(accounts: &[AccountInfo], at_least_len: usi } #[inline(always)] -pub fn check_custodian_owner_is_program_id(custodian: &AccountInfo) -> Result<()> { - require_eq!(custodian.owner, &ID, ErrorCode::ConstraintOwner); +pub fn require_owned_by_this_program(account: &AccountInfo, account_name: &str) -> Result<()> { + if account.owner != &ID { + return Err(ErrorCode::ConstraintOwner.into()) + .map_err(|e: Error| e.with_account_name(account_name)); + } + Ok(()) } +#[inline(always)] +pub fn try_custodian_account( + custodian_info: &AccountInfo, + check_if_paused: bool, +) -> Result> { + super::helpers::require_owned_by_this_program(custodian_info, "custodian")?; + + let custodian = + Custodian::try_deserialize(&mut &custodian_info.data.borrow()[..]).map(Box::new)?; + + // Make sure the custodian is not paused. + if check_if_paused && custodian.paused { + return Err(MatchingEngineError::Paused.into()); + } + + Ok(custodian) +} + +#[inline(always)] +pub fn try_auction_config_account( + auction_config_info: &AccountInfo, + expected_config_id: Option, +) -> Result> { + super::helpers::require_owned_by_this_program(auction_config_info, "auction_config")?; + + let auction_config = + AuctionConfig::try_deserialize(&mut &auction_config_info.data.borrow()[..]) + .map(Box::new)?; + + // Make sure the custodian is not paused. + if let Some(expected_config_id) = expected_config_id { + if auction_config.id != expected_config_id { + msg!("Auction config id is invalid"); + return Err(ErrorCode::ConstraintRaw.into()) + .map_err(|e: Error| e.with_account_name("auction_config")); + } + } + + Ok(auction_config) +} + +#[inline(always)] +pub fn try_live_endpoint_account( + endpoint_info: &AccountInfo, + endpoint_name: &str, +) -> Result> { + super::helpers::require_owned_by_this_program(endpoint_info, endpoint_name)?; + + let endpoint = + RouterEndpoint::try_deserialize(&mut &endpoint_info.data.borrow()[..]).map(Box::new)?; + + if endpoint.protocol == MessageProtocol::None { + return Err(MatchingEngineError::EndpointDisabled.into()); + } + + Ok(endpoint) +} + +#[inline(always)] +pub fn try_live_endpoint_accounts_path( + from_endpoint_info: &AccountInfo, + to_endpoint_info: &AccountInfo, +) -> Result<(Box, Box)> { + let from_endpoint = try_live_endpoint_account(from_endpoint_info, "from_endpoint")?; + let to_endpoint = try_live_endpoint_account(to_endpoint_info, "to_endpoint")?; + + if from_endpoint.chain == to_endpoint.chain { + return Err(MatchingEngineError::SameEndpoint.into()); + } + + Ok((from_endpoint, to_endpoint)) +} + +pub fn try_usdc_account<'a, 'b>(usdc_info: &'a AccountInfo<'b>) -> Result<&'a AccountInfo<'b>> { + if usdc_info.key != &common::USDC_MINT { + return Err(MatchingEngineError::InvalidMint.into()) + .map_err(|e: Error| e.with_account_name("usdc")); + } + + Ok(usdc_info) +} + +/// Read from an account info +pub fn try_fast_market_order_account<'a>( + fast_market_order_info: &'a AccountInfo, +) -> Result> { + let data = fast_market_order_info.data.borrow(); + + if data.len() < 8 { + return Err(ErrorCode::AccountDiscriminatorNotFound.into()); + } + + if &data[0..8] != &FastMarketOrder::DISCRIMINATOR { + return Err(ErrorCode::AccountDiscriminatorMismatch.into()); + } + + // TODO: Move up? + super::helpers::require_owned_by_this_program(fast_market_order_info, "fast_market_order")?; + + Ok(Ref::map(data, |data| { + bytemuck::from_bytes(&data[8..8 + std::mem::size_of::()]) + })) +} + pub fn create_account_reliably( payer_key: &Pubkey, account_key: &Pubkey, @@ -180,3 +295,72 @@ pub fn create_usdc_token_account_reliably( solana_program::program::invoke_signed_unchecked(&init_token_account_ix, accounts, &[]) } + +/// VaaMessageBodyHeader for the digest calculation +/// +/// This is the header of the vaa message body. It is used to calculate the +/// digest of the fast market order. +#[derive(Debug)] +pub struct VaaMessageBodyHeader { + pub consistency_level: u8, + pub timestamp: u32, + pub sequence: u64, + pub emitter_chain: u16, + pub emitter_address: [u8; 32], +} + +impl VaaMessageBodyHeader { + // TODO: Remove + pub fn new( + consistency_level: u8, + timestamp: u32, + sequence: u64, + emitter_chain: u16, + emitter_address: [u8; 32], + ) -> Self { + Self { + consistency_level, + timestamp, + sequence, + emitter_chain, + emitter_address, + } + } + + /// This function creates both the message body for the fast market order, including the payload. + pub fn message_body(&self, fast_market_order: &FastMarketOrder) -> Vec { + let mut message_body = vec![]; + message_body.extend_from_slice(&self.timestamp.to_be_bytes()); + message_body.extend_from_slice(&[0, 0, 0, 0]); // 0 nonce + message_body.extend_from_slice(&self.emitter_chain.to_be_bytes()); + message_body.extend_from_slice(&self.emitter_address); + message_body.extend_from_slice(&self.sequence.to_be_bytes()); + message_body.extend_from_slice(&[self.consistency_level]); + message_body.push(11_u8); + message_body.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer); + message_body.extend_from_slice(&fast_market_order.sender); + message_body.extend_from_slice(&fast_market_order.refund_address); + message_body.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); + message_body.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); + if fast_market_order.redeemer_message_length > 0 { + message_body.extend_from_slice( + &fast_market_order.redeemer_message + [..usize::from(fast_market_order.redeemer_message_length)], + ); + } + message_body + } + + /// The digest is the hash of the message hash. + pub fn digest(&self, fast_market_order: &FastMarketOrder) -> keccak::Hash { + wormhole_svm_definitions::compute_keccak_digest( + keccak::hashv(&[&self.message_body(fast_market_order)]), + None, + ) + } +} diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index 47b788f8e..a829f93e3 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -1,8 +1,10 @@ pub mod burn_and_post; pub mod close_fast_market_order; +// TODO: Rename module to "execute_order_cctp". pub mod execute_order; pub mod helpers; pub mod initialize_fast_market_order; +// TODO: Rename module to "place_initial_offer_cctp". pub mod place_initial_offer; pub mod prepare_order_response; pub mod process_instruction; diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index e42b92846..7636ede29 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -1,37 +1,31 @@ -use super::helpers::*; -use crate::state::MessageProtocol; -use crate::state::{ - Auction, AuctionConfig, AuctionInfo, AuctionStatus, Custodian, - FastMarketOrder as FastMarketOrderState, RouterEndpoint, -}; -use crate::ID as PROGRAM_ID; use anchor_lang::prelude::*; -use anchor_lang::Discriminator; use anchor_spl::token::spl_token; use bytemuck::{Pod, Zeroable}; use common::TRANSFER_AUTHORITY_SEED_PREFIX; -use solana_program::instruction::Instruction; -use solana_program::keccak; -use solana_program::program::invoke_signed_unchecked; +use solana_program::{instruction::Instruction, program::invoke_signed_unchecked}; + +use crate::{ + error::MatchingEngineError, + state::{Auction, AuctionInfo, AuctionStatus, MessageProtocol}, + ID, +}; use super::FallbackMatchingEngineInstruction; -use crate::error::MatchingEngineError; +// TODO: Remove this. +pub use super::helpers::VaaMessageBodyHeader; + +// TODO: Remove this struct. Just use u64. #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct PlaceInitialOfferCctpShimData { pub offer_price: u64, } -impl PlaceInitialOfferCctpShimData { - pub fn from_bytes(data: &[u8]) -> Option<&Self> { - bytemuck::try_from_bytes::(data).ok() - } -} - #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct PlaceInitialOfferCctpShimAccounts<'ix> { /// The signer account + // TODO: Rename to "payer". pub signer: &'ix Pubkey, /// The transfer authority account pub transfer_authority: &'ix Pubkey, @@ -43,42 +37,27 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub from_endpoint: &'ix Pubkey, /// The to endpoint account pub to_endpoint: &'ix Pubkey, - /// The fast market order account, which will be initialized. Seeds are [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] + /// The fast market order account, which will be initialized. Seeds are + /// [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] pub fast_market_order: &'ix Pubkey, - /// The auction account, which will be initialized + /// The auction account, which will be initialized. + // TODO: Rename to "new_auction". pub auction: &'ix Pubkey, /// The offer token account pub offer_token: &'ix Pubkey, - /// The auction custody token account + /// The auction custody token account. + // TODO: Rename to "new_auction_custody". pub auction_custody_token: &'ix Pubkey, /// The usdc token account pub usdc: &'ix Pubkey, /// The system program account + // TODO: Remove. pub system_program: &'ix Pubkey, /// The token program account + // TODO: Remove. pub token_program: &'ix Pubkey, } -impl<'ix> PlaceInitialOfferCctpShimAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new(*self.signer, true), - AccountMeta::new_readonly(*self.transfer_authority, false), - AccountMeta::new_readonly(*self.custodian, false), - AccountMeta::new_readonly(*self.auction_config, false), - AccountMeta::new_readonly(*self.from_endpoint, false), - AccountMeta::new_readonly(*self.to_endpoint, false), - AccountMeta::new_readonly(*self.fast_market_order, false), - AccountMeta::new(*self.auction, false), - AccountMeta::new(*self.offer_token, false), - AccountMeta::new(*self.auction_custody_token, false), - AccountMeta::new_readonly(*self.usdc, false), - AccountMeta::new_readonly(*self.system_program, false), - AccountMeta::new_readonly(*self.token_program, false), - ] - } -} - #[derive(Debug, Clone, Copy)] pub struct PlaceInitialOfferCctpShim<'ix> { pub program_id: &'ix Pubkey, @@ -88,223 +67,102 @@ pub struct PlaceInitialOfferCctpShim<'ix> { impl PlaceInitialOfferCctpShim<'_> { pub fn instruction(&self) -> Instruction { + let PlaceInitialOfferCctpShimAccounts { + signer: payer, + transfer_authority, + custodian, + auction_config, + from_endpoint, + to_endpoint, + fast_market_order, + auction: new_auction, + offer_token, + auction_custody_token: new_auction_custody, + usdc, + system_program: _, + token_program: _, + } = self.accounts; + Instruction { program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), + accounts: vec![ + AccountMeta::new(*payer, true), + AccountMeta::new_readonly(*transfer_authority, false), + AccountMeta::new_readonly(*custodian, false), + AccountMeta::new_readonly(*auction_config, false), + AccountMeta::new_readonly(*from_endpoint, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new(*new_auction, false), + AccountMeta::new(*offer_token, false), + AccountMeta::new(*new_auction_custody, false), + AccountMeta::new_readonly(*usdc, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + ], data: FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(&self.data).to_vec(), } } } -/// VaaMessageBodyHeader for the digest calculation -/// -/// This is the header of the vaa message body. It is used to calculate the digest of the fast market order. -#[derive(Debug)] -pub struct VaaMessageBodyHeader { - pub consistency_level: u8, - pub vaa_time: u32, - pub nonce: u32, - pub sequence: u64, - pub emitter_chain: u16, - pub emitter_address: [u8; 32], -} - -impl VaaMessageBodyHeader { - pub fn new( - consistency_level: u8, - vaa_time: u32, - sequence: u64, - emitter_chain: u16, - emitter_address: [u8; 32], - ) -> Self { - Self { - consistency_level, - vaa_time, - nonce: 0, - sequence, - emitter_chain, - emitter_address, - } - } - - /// This function creates both the message body for the fast market order, including the payload. - pub fn message_body(&self, fast_market_order: &FastMarketOrderState) -> Vec { - let mut message_body = vec![]; - message_body.extend_from_slice(&self.vaa_time.to_be_bytes()); - message_body.extend_from_slice(&self.nonce.to_be_bytes()); - message_body.extend_from_slice(&self.emitter_chain.to_be_bytes()); - message_body.extend_from_slice(&self.emitter_address); - message_body.extend_from_slice(&self.sequence.to_be_bytes()); - message_body.extend_from_slice(&[self.consistency_level]); - message_body.push(11_u8); - message_body.extend_from_slice(&fast_market_order.amount_in.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.min_amount_out.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.target_chain.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.redeemer); - message_body.extend_from_slice(&fast_market_order.sender); - message_body.extend_from_slice(&fast_market_order.refund_address); - message_body.extend_from_slice(&fast_market_order.max_fee.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.init_auction_fee.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.deadline.to_be_bytes()); - message_body.extend_from_slice(&fast_market_order.redeemer_message_length.to_be_bytes()); - if fast_market_order.redeemer_message_length > 0 { - message_body.extend_from_slice( - &fast_market_order.redeemer_message - [..usize::from(fast_market_order.redeemer_message_length)], - ); - } - message_body - } - - /// This function creates the hash of the message body for the fast market order. - /// This is used to create the digest. - pub fn message_hash(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { - keccak::hashv(&[self.message_body(fast_market_order).as_ref()]) - } - - /// The digest is the hash of the message hash. - pub fn digest(&self, fast_market_order: &FastMarketOrderState) -> keccak::Hash { - keccak::hashv(&[self.message_hash(fast_market_order).as_ref()]) - } - - /// This function returns the vaa time. - pub fn vaa_time(&self) -> u32 { - self.vaa_time - } - - /// This function returns the sequence number of the fast market order. - pub fn sequence(&self) -> u64 { - self.sequence - } - - /// This function returns the emitter chain of the fast market order. - pub fn emitter_chain(&self) -> u16 { - self.emitter_chain - } -} - -pub fn place_initial_offer_cctp_shim( - accounts: &[AccountInfo], - data: &PlaceInitialOfferCctpShimData, -) -> Result<()> { - let program_id = &PROGRAM_ID; // Your program ID - +pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { // Check all accounts are valid - require_min_account_infos_len(accounts, 11)?; - // Extract data fields - let PlaceInitialOfferCctpShimData { offer_price } = *data; - - let signer = &accounts[0]; + super::helpers::require_min_account_infos_len(accounts, 11)?; + + // This instruction will use the payer to create the following accounts: + // 1. Auction. + // 2. Auction Custody Token Account. + let payer_info = &accounts[0]; + + // This transfer authority must have been delegated authority to transfer + // USDC so it can transfer tokens to the auction custody token account. + // + // We will validate this transfer authority when we will transfer USDC to + // the auction's custody account. let transfer_authority = &accounts[1]; - let custodian = &accounts[2]; - let auction_config = &accounts[3]; - let from_endpoint = &accounts[4]; - let to_endpoint = &accounts[5]; - let fast_market_order_account = &accounts[6]; - let auction_account = &accounts[7]; - let auction_key = auction_account.key(); - let offer_token = &accounts[8]; - let auction_custody_token = &accounts[9]; - let usdc = &accounts[10]; - - // Check that the fast market order account is owned by the program - if fast_market_order_account.owner != program_id { - msg!("Fast market order account owner is invalid"); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order_account")); - } - let fast_market_order_data = &fast_market_order_account.data.borrow()[..]; - let fast_market_order_zero_copy = FastMarketOrderState::try_read(fast_market_order_data)?; - - let vaa_time = fast_market_order_zero_copy.vaa_timestamp; - let sequence = fast_market_order_zero_copy.vaa_sequence; - let consistency_level = fast_market_order_zero_copy.vaa_consistency_level; - - // Check pda of the transfer authority is valid - let transfer_authority_seeds = [ - TRANSFER_AUTHORITY_SEED_PREFIX, - auction_key.as_ref(), - &offer_price.to_be_bytes(), - ]; - let (transfer_authority_pda, transfer_authority_bump) = - Pubkey::find_program_address(&transfer_authority_seeds, &PROGRAM_ID); - if transfer_authority_pda != transfer_authority.key() { - msg!("Transfer authority pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys((transfer_authority_pda, transfer_authority.key())) - }); - } + let custodian = super::helpers::try_custodian_account( + &accounts[2], + true, // check_if_paused + )?; - // Check custodian owner - check_custodian_owner_is_program_id(custodian)?; + let auction_config = super::helpers::try_auction_config_account( + &accounts[3], + Some(custodian.auction_config_id), + )?; - // Check custodian is not paused - let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..])?; - if checked_custodian.paused { - msg!("Custodian is paused"); - return Err(MatchingEngineError::Paused.into()) - .map_err(|e: Error| e.with_account_name("custodian")); - } - // Check auction_config owner - if auction_config.owner != program_id { - msg!( - "Auction config owner is invalid: expected {}, got {}", - program_id, - auction_config.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("auction_config")); - } + let (from_endpoint_account, to_endpoint_account) = + super::helpers::try_live_endpoint_accounts_path(&accounts[4], &accounts[5])?; - // Check auction config id is correct corresponding to the custodian - let auction_config_account = - AuctionConfig::try_deserialize(&mut &auction_config.data.borrow()[..])?; - if auction_config_account.id != checked_custodian.auction_config_id { - msg!("Auction config id is invalid"); - return Err(ErrorCode::ConstraintRaw.into()) - .map_err(|e: Error| e.with_account_name("auction_config")); - } + let fast_market_order = super::helpers::try_fast_market_order_account(&accounts[6])?; - // Check usdc mint - if usdc.key() != common::USDC_MINT { - msg!("Usdc mint is invalid"); - return Err(MatchingEngineError::InvalidMint.into()); - } + // Verify the fast market order comes from a registered endpoint. + // TODO: Consider moving source endpoint check when creating fast market + // order account. + require_eq!( + from_endpoint_account.chain, + fast_market_order.vaa_emitter_chain, + MatchingEngineError::InvalidSourceRouter + ); - // Check from_endpoint owner - if from_endpoint.owner != program_id { - msg!( - "From endpoint owner is invalid: expected {}, got {}", - program_id, - from_endpoint.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("from_endpoint")); + if from_endpoint_account.address != fast_market_order.vaa_emitter_address { + return Err(MatchingEngineError::InvalidSourceRouter.into()); } - // Deserialise the from_endpoint account - let from_endpoint_account = - RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..])?; - - // Check to_endpoint owner - if to_endpoint.owner != program_id { - msg!( - "To endpoint owner is invalid: expected {}, got {}", - program_id, - to_endpoint.owner - ); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("to_endpoint")); - } + // Verify that the target chain has a registered endpoint. + require_eq!( + to_endpoint_account.chain, + fast_market_order.target_chain, + MatchingEngineError::InvalidTargetRouter + ); - // Deserialise the to_endpoint account - let to_endpoint_account = RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..])?; + let new_auction_info = &accounts[7]; + let new_auction_key = new_auction_info.key; - // Check that the from and to endpoints are different - if from_endpoint_account.chain == to_endpoint_account.chain { - return Err(MatchingEngineError::SameEndpoint.into()); - } + // This account must be the USDC mint. This instruction does not refer to + // this account explicitly. It just needs to exist so that we can create the + // auction's custody token account. + super::helpers::try_usdc_account(&accounts[10])?; // Check that the to endpoint is a valid protocol match to_endpoint_account.protocol { @@ -312,200 +170,174 @@ pub fn place_initial_offer_cctp_shim( _ => return Err(MatchingEngineError::InvalidEndpoint.into()), } - // Check that the vaa emitter address equals the from_endpoints encoded address - if from_endpoint_account.address != fast_market_order_zero_copy.vaa_emitter_address { - msg!("Vaa emitter address is not equal to the from_endpoints encoded address"); - return Err(MatchingEngineError::InvalidSourceRouter.into()); - } - - // Check that the vaa emitter chain is equal to the from_endpoints chain - if from_endpoint_account.chain != fast_market_order_zero_copy.vaa_emitter_chain { - msg!("Vaa emitter chain is not equal to the from_endpoints chain"); - return Err(MatchingEngineError::InvalidSourceRouter.into()); - } - - // Check that to endpoint chain is equal to the fast_market_order target_chain - if to_endpoint_account.chain != fast_market_order_zero_copy.target_chain { - msg!("To endpoint chain is not equal to the fast_market_order target_chain"); - return Err(MatchingEngineError::InvalidTargetRouter.into()); - } + let offer_price = data.offer_price; + let vaa_timestamp = fast_market_order.vaa_timestamp; // Check contents of fast_market_order + // TODO: Use shared method that both place initial offer instructions can + // use. { - let deadline = i64::from(fast_market_order_zero_copy.deadline); - let expiration = i64::from(vaa_time).saturating_add(crate::VAA_AUCTION_EXPIRATION_TIME); + let deadline = i64::from(fast_market_order.deadline); + let expiration = crate::VAA_AUCTION_EXPIRATION_TIME.saturating_add(vaa_timestamp.into()); let current_time: i64 = Clock::get().unwrap().unix_timestamp; if !((deadline == 0 || current_time < deadline) && current_time < expiration) { msg!("Fast market order has expired"); return Err(MatchingEngineError::FastMarketOrderExpired.into()); } - if offer_price > fast_market_order_zero_copy.max_fee { + if offer_price > fast_market_order.max_fee { msg!("Offer price is too high"); return Err(MatchingEngineError::OfferPriceTooHigh.into()); } } - // Create the vaa_message struct to get the digest - let vaa_message = VaaMessageBodyHeader::new( - consistency_level, - vaa_time, - sequence, - from_endpoint_account.chain, - from_endpoint_account.address, - ); - let vaa_message_digest = vaa_message.digest(fast_market_order_zero_copy); + // We will need to move USDC from the offer token account to the custody + // token account. The custody token account will need to be created first. + let offer_token_info = &accounts[8]; + let new_auction_custody_info = &accounts[9]; - // Begin of initialisation of auction custody token account - // ------------------------------------------------------------------------------------------------ - let (auction_custody_token_pda, auction_custody_token_bump) = Pubkey::find_program_address( + // TODO: Double-check that we do not need verify the derived pubkey is + // correct. We shouldn't have to because the seeds are used to create the + // account, which will only work if the auction custody pubkey is correct. + let (_, new_auction_custody_bump) = Pubkey::find_program_address( &[ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction_key.as_ref(), + new_auction_key.as_ref(), ], - program_id, + &ID, ); - if auction_custody_token_pda != auction_custody_token.key() { - msg!( - "Auction custody token pda is invalid. Passed account: {}, expected: {}", - auction_custody_token.key(), - auction_custody_token_pda - ); - return Err(MatchingEngineError::InvalidPda.into()); - } - let auction_custody_token_seeds = [ - crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - auction_key.as_ref(), - &[auction_custody_token_bump], - ]; - let auction_custody_token_signer_seeds = &[&auction_custody_token_seeds[..]]; - - create_usdc_token_account_reliably( - &signer.key(), - &auction_custody_token_pda, - &auction_account.key(), - auction_custody_token.lamports(), + super::helpers::create_usdc_token_account_reliably( + payer_info.key, + new_auction_custody_info.key, + new_auction_info.key, + new_auction_custody_info.lamports(), accounts, - auction_custody_token_signer_seeds, + &[&[ + crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, + new_auction_key.as_ref(), + &[new_auction_custody_bump], + ]], )?; - // ------------------------------------------------------------------------------------------------ - // End of initialisation of auction custody token account - - // Begin of initialisation of auction account - // ------------------------------------------------------------------------------------------------ - let auction_space = 8 + Auction::INIT_SPACE; - let (pda, bump) = Pubkey::find_program_address( - &[Auction::SEED_PREFIX, vaa_message_digest.as_ref()], - program_id, + // TODO: Double-check that we do not need verify the derived pubkey is + // correct. We shouldn't have to because the seeds are used to transfer + // tokens, which will only work if the transfer authority pubkey is correct. + let (_, transfer_authority_bump) = Pubkey::find_program_address( + &[ + TRANSFER_AUTHORITY_SEED_PREFIX, + new_auction_key.as_ref(), + &offer_price.to_be_bytes(), + ], + &ID, ); - if pda != auction_key { - msg!("Auction pda is invalid"); - return Err(MatchingEngineError::InvalidPda.into()); - } - let auction_seeds = [Auction::SEED_PREFIX, vaa_message_digest.as_ref(), &[bump]]; - let auction_signer_seeds = &[&auction_seeds[..]]; - create_account_reliably( - &signer.key(), - &auction_key, - auction_account.lamports(), - auction_space, - accounts, - program_id, - auction_signer_seeds, - )?; - // Borrow the account data mutably - let mut data = auction_account - .try_borrow_mut_data() - .map_err(|_| MatchingEngineError::AccountNotWritable)?; - - // Write the discriminator to the first 8 bytes - let discriminator = Auction::discriminator(); - data[0..8].copy_from_slice(&discriminator); - - let security_deposit = fast_market_order_zero_copy.max_fee.saturating_add( + // The total amount being transferred to the auction's custody token account + // is the order's amount and auction participant's security deposit. + let security_deposit = fast_market_order.max_fee.saturating_add( crate::utils::auction::compute_notional_security_deposit( - &auction_config_account.parameters, - fast_market_order_zero_copy.amount_in, + &auction_config, + fast_market_order.amount_in, ), ); - let auction_to_write = Auction { - bump, - vaa_hash: vaa_message - .digest(fast_market_order_zero_copy) - .as_ref() - .try_into() - .unwrap(), - vaa_timestamp: vaa_message.vaa_time(), - target_protocol: to_endpoint_account.protocol, - status: AuctionStatus::Active, - prepared_by: signer.key(), - info: AuctionInfo { - config_id: auction_config_account.id, - custody_token_bump: auction_custody_token_bump, - vaa_sequence: vaa_message.sequence(), - source_chain: vaa_message.emitter_chain(), - best_offer_token: offer_token.key(), - initial_offer_token: offer_token.key(), - start_slot: Clock::get().unwrap().slot, - amount_in: fast_market_order_zero_copy.amount_in, - security_deposit, - offer_price, - redeemer_message_len: fast_market_order_zero_copy.redeemer_message_length, - destination_asset_info: Default::default(), - } - .into(), - }; - // Write the auction struct to the account - let auction_bytes = auction_to_write - .try_to_vec() - .map_err(|_| MatchingEngineError::BorshDeserializationError)?; - data[8..8_usize.saturating_add(auction_bytes.len())].copy_from_slice(&auction_bytes); - // ------------------------------------------------------------------------------------------------ - // End of initialisation of auction account - - // Start of token transfer from offer token to auction custody token - // ------------------------------------------------------------------------------------------------ - let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, - &offer_token.key(), - &auction_custody_token.key(), - &transfer_authority.key(), - &[], // Apparently this is only for multi-sig accounts - fast_market_order_zero_copy + offer_token_info.key, + new_auction_custody_info.key, + transfer_authority.key, + &[], + fast_market_order .amount_in .checked_add(security_deposit) .ok_or_else(|| MatchingEngineError::U64Overflow)?, ) .unwrap(); + invoke_signed_unchecked( &transfer_ix, accounts, &[&[ TRANSFER_AUTHORITY_SEED_PREFIX, - auction_key.as_ref(), + new_auction_key.as_ref(), &offer_price.to_be_bytes(), &[transfer_authority_bump], ]], )?; - // ------------------------------------------------------------------------------------------------ - // End of token transfer from offer token to auction custody token - Ok(()) + + let vaa_sequence = fast_market_order.vaa_sequence; + let consistency_level = fast_market_order.vaa_consistency_level; + + // Generate the VAA digest. This digest is used as the seed for the newly + // created auction account. + let vaa_message_digest = super::helpers::VaaMessageBodyHeader { + consistency_level, + timestamp: vaa_timestamp, + sequence: vaa_sequence, + emitter_chain: from_endpoint_account.chain, + emitter_address: from_endpoint_account.address, + } + .digest(&fast_market_order); + + // TODO: Double-check that we do not need verify the derived pubkey is + // correct. We shouldn't have to because the seeds are used to create the + // account, which will only work if the auction pubkey is correct. + let (_, new_auction_bump) = + Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_message_digest.0], &ID); + + // Create the auction account and serialize its data into it. + super::helpers::create_account_reliably( + payer_info.key, + new_auction_key, + new_auction_info.lamports(), + 8 + Auction::INIT_SPACE, + accounts, + &ID, + &[&[ + Auction::SEED_PREFIX, + &vaa_message_digest.0, + &[new_auction_bump], + ]], + )?; + + let new_auction_info_data: &mut [u8] = &mut new_auction_info.data.borrow_mut(); + let mut new_auction_cursor = std::io::Cursor::new(new_auction_info_data); + + Auction { + bump: new_auction_bump, + vaa_hash: vaa_message_digest.0, + vaa_timestamp, + target_protocol: to_endpoint_account.protocol, + status: AuctionStatus::Active, + prepared_by: *payer_info.key, + info: AuctionInfo { + config_id: auction_config.id, + custody_token_bump: new_auction_custody_bump, + vaa_sequence, + source_chain: from_endpoint_account.chain, + best_offer_token: *offer_token_info.key, + initial_offer_token: *offer_token_info.key, + start_slot: Clock::get().unwrap().slot, + amount_in: fast_market_order.amount_in, + security_deposit, + offer_price, + redeemer_message_len: fast_market_order.redeemer_message_length, + destination_asset_info: Default::default(), + } + .into(), + } + .try_serialize(&mut new_auction_cursor) } #[cfg(test)] mod tests { - use crate::state::FastMarketOrderParams; + use crate::state::{FastMarketOrder, FastMarketOrderParams}; use super::*; #[test] fn test_bytemuck() { - let test_fast_market_order = FastMarketOrderState::new(FastMarketOrderParams { + let test_fast_market_order = FastMarketOrder::new(FastMarketOrderParams { amount_in: 1000000000000000000, min_amount_out: 1000000000000000000, deadline: 1000000000, @@ -526,6 +358,6 @@ mod tests { vaa_emitter_address: [0_u8; 32], }); let bytes = bytemuck::bytes_of(&test_fast_market_order); - assert!(bytes.len() == std::mem::size_of::()); + assert!(bytes.len() == std::mem::size_of::()); } } diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 76542fada..dd9ccc8b0 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -1,17 +1,12 @@ use std::io::Cursor; -use super::helpers::create_account_reliably; -use super::place_initial_offer::VaaMessageBodyHeader; +use super::helpers::{create_account_reliably, VaaMessageBodyHeader}; use super::FallbackMatchingEngineInstruction; -use crate::fallback::helpers::check_custodian_owner_is_program_id; use crate::fallback::helpers::create_usdc_token_account_reliably; use crate::fallback::helpers::require_min_account_infos_len; use crate::state::PreparedOrderResponseInfo; use crate::state::PreparedOrderResponseSeeds; -use crate::state::{ - Custodian, FastMarketOrder as FastMarketOrderState, MessageProtocol, PreparedOrderResponse, - RouterEndpoint, -}; +use crate::state::{Custodian, MessageProtocol, PreparedOrderResponse, RouterEndpoint}; use crate::CCTP_MINT_RECIPIENT; use crate::ID; use anchor_lang::prelude::*; @@ -45,14 +40,15 @@ pub struct FinalizedVaaMessageArgs { } impl FinalizedVaaMessageArgs { + // TODO: Change return type to keccak::Hash pub fn digest( &self, vaa_message_body_header: VaaMessageBodyHeader, deposit_vaa_payload: Deposit, ) -> [u8; 32] { let message_hash = keccak::hashv(&[ - vaa_message_body_header.vaa_time.to_be_bytes().as_ref(), - vaa_message_body_header.nonce.to_be_bytes().as_ref(), + vaa_message_body_header.timestamp.to_be_bytes().as_ref(), + [0, 0, 0, 0].as_ref(), // 0 nonce vaa_message_body_header.emitter_chain.to_be_bytes().as_ref(), &vaa_message_body_header.emitter_address, &vaa_message_body_header.sequence.to_be_bytes(), @@ -60,10 +56,7 @@ impl FinalizedVaaMessageArgs { deposit_vaa_payload.to_vec().as_ref(), ]); // Digest is the hash of the message - keccak::hashv(&[message_hash.as_ref()]) - .as_ref() - .try_into() - .unwrap() + keccak::hashv(&[message_hash.as_ref()]).0 } } @@ -208,11 +201,9 @@ pub fn prepare_order_response_cctp_shim( let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; - // Load accounts - let fast_market_order_account_data = &fast_market_order.data.borrow()[..]; let fast_market_order_zero_copy = - FastMarketOrderState::try_read(fast_market_order_account_data)?; + super::helpers::try_fast_market_order_account(fast_market_order)?; // Create pdas for addresses that need to be created // Check the prepared order response account is valid let fast_market_order_digest = fast_market_order_zero_copy.digest(); @@ -230,7 +221,7 @@ pub fn prepare_order_response_cctp_shim( ); // Check custodian owner - check_custodian_owner_is_program_id(custodian)?; + super::helpers::require_owned_by_this_program(custodian, "custodian")?; // Check that custodian deserializes correctly let _checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index 2670003cd..b5c74482c 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -5,7 +5,7 @@ use crate::ID; use super::execute_order::handle_execute_order_shim; use super::initialize_fast_market_order::InitializeFastMarketOrderData; -use super::place_initial_offer::{place_initial_offer_cctp_shim, PlaceInitialOfferCctpShimData}; +use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::prepare_order_response::{ prepare_order_response_cctp_shim, PrepareOrderResponseCctpShimData, }; @@ -54,7 +54,7 @@ pub fn process_instruction( super::close_fast_market_order::process(accounts) } FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(data) => { - place_initial_offer_cctp_shim(accounts, data) + super::place_initial_offer::process(accounts, data) } FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { handle_execute_order_shim(accounts) diff --git a/solana/programs/matching-engine/src/state/fast_market_order.rs b/solana/programs/matching-engine/src/state/fast_market_order.rs index 8a3455d41..071fb4ffd 100644 --- a/solana/programs/matching-engine/src/state/fast_market_order.rs +++ b/solana/programs/matching-engine/src/state/fast_market_order.rs @@ -1,4 +1,4 @@ -use anchor_lang::{prelude::*, Discriminator}; +use anchor_lang::prelude::*; use solana_program::keccak; /// An account that represents a fast market order VAA. It is created by the @@ -143,18 +143,4 @@ impl FastMarketOrder { ) .0 } - - /// Read from an account info - pub fn try_read(data: &[u8]) -> Result<&Self> { - if data.len() < 8 { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - let discriminator: [u8; 8] = data[0..8].try_into().unwrap(); - if discriminator != Self::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - let byte_muck_data = &data[8..]; - let fast_market_order = bytemuck::from_bytes::(byte_muck_data); - Ok(fast_market_order) - } } From 6ad6479abe0c9376b49219b853301afd5bde3cc9 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Mon, 12 May 2025 09:33:06 -0500 Subject: [PATCH 102/112] initialize fast market order fixup --- .../fallback/processor/initialize_fast_market_order.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index aaab935aa..122a69703 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -109,7 +109,6 @@ pub(super) fn process( // Verify that the fast market order account's key is derived correctly. let new_fast_market_order_info = &accounts[1]; - let fast_market_order_key = new_fast_market_order_info.key; let (expected_fast_market_order_key, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrder::SEED_PREFIX, @@ -119,13 +118,6 @@ pub(super) fn process( &ID, ); - if fast_market_order_key != &expected_fast_market_order_key { - return Err(MatchingEngineError::InvalidPda.into()).map_err(|e: Error| { - e.with_account_name("fast_market_order") - .with_pubkeys((*fast_market_order_key, expected_fast_market_order_key)) - }); - } - // These accounts will be used by the Verify VAA shim program. let wormhole_guardian_set_info = &accounts[2]; let shim_guardian_signatures_info = &accounts[3]; @@ -157,7 +149,7 @@ pub(super) fn process( super::helpers::create_account_reliably( payer_info.key, - fast_market_order_key, + &expected_fast_market_order_key, new_fast_market_order_info.lamports(), FAST_MARKET_ORDER_DATA_LEN, accounts, From c101a37d5c450ea906e6c8b77195ddf7f9dfb83f Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Mon, 12 May 2025 09:33:15 -0500 Subject: [PATCH 103/112] solana: more place initial offer fixes --- .../processor/initialize_fast_market_order.rs | 2 +- .../fallback/processor/place_initial_offer.rs | 78 +++++++++---------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index 122a69703..a968959c0 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -3,7 +3,7 @@ use bytemuck::{Pod, Zeroable}; use solana_program::{instruction::Instruction, keccak, program::invoke_signed_unchecked}; use wormhole_svm_shim::verify_vaa; -use crate::{error::MatchingEngineError, state::FastMarketOrder, ID}; +use crate::{state::FastMarketOrder, ID}; pub struct InitializeFastMarketOrderAccounts<'ix> { /// Lamports from this signer will be used to create the new fast market diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 7636ede29..29b7b1678 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -22,6 +22,7 @@ pub struct PlaceInitialOfferCctpShimData { pub offer_price: u64, } +// TODO: Rename to "PlaceInitialOfferCctpV2Accounts". #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct PlaceInitialOfferCctpShimAccounts<'ix> { /// The signer account @@ -58,6 +59,7 @@ pub struct PlaceInitialOfferCctpShimAccounts<'ix> { pub token_program: &'ix Pubkey, } +// TODO: Rename to "PlaceInitialOfferCctpV2". #[derive(Debug, Clone, Copy)] pub struct PlaceInitialOfferCctpShim<'ix> { pub program_id: &'ix Pubkey, @@ -117,9 +119,9 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - // This transfer authority must have been delegated authority to transfer // USDC so it can transfer tokens to the auction custody token account. // - // We will validate this transfer authority when we will transfer USDC to - // the auction's custody account. - let transfer_authority = &accounts[1]; + // We will validate this transfer authority when we attempt to transfer USDC + // to the auction's custody account. + let _transfer_authority = &accounts[1]; let custodian = super::helpers::try_custodian_account( &accounts[2], @@ -157,7 +159,26 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - ); let new_auction_info = &accounts[7]; - let new_auction_key = new_auction_info.key; + + let vaa_sequence = fast_market_order.vaa_sequence; + let vaa_timestamp = fast_market_order.vaa_timestamp; + let consistency_level = fast_market_order.vaa_consistency_level; + + // Generate the VAA digest. This digest is used as the seed for the newly + // created auction account. + let vaa_message_digest = super::helpers::VaaMessageBodyHeader { + consistency_level, + timestamp: vaa_timestamp, + sequence: vaa_sequence, + emitter_chain: from_endpoint_account.chain, + emitter_address: from_endpoint_account.address, + } + .digest(&fast_market_order); + + // Derive the expected auction account key. This key is used for the auction + // custody token account seed. + let (expected_auction_key, new_auction_bump) = + Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_message_digest.0], &ID); // This account must be the USDC mint. This instruction does not refer to // this account explicitly. It just needs to exist so that we can create the @@ -171,7 +192,6 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - } let offer_price = data.offer_price; - let vaa_timestamp = fast_market_order.vaa_timestamp; // Check contents of fast_market_order // TODO: Use shared method that both place initial offer instructions can @@ -196,37 +216,35 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - let offer_token_info = &accounts[8]; let new_auction_custody_info = &accounts[9]; - // TODO: Double-check that we do not need verify the derived pubkey is - // correct. We shouldn't have to because the seeds are used to create the - // account, which will only work if the auction custody pubkey is correct. - let (_, new_auction_custody_bump) = Pubkey::find_program_address( + // We will use the expected auction custody token account key to create this + // account. + let (expected_auction_custody_key, new_auction_custody_bump) = Pubkey::find_program_address( &[ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - new_auction_key.as_ref(), + expected_auction_key.as_ref(), ], &ID, ); super::helpers::create_usdc_token_account_reliably( payer_info.key, - new_auction_custody_info.key, + &expected_auction_custody_key, new_auction_info.key, new_auction_custody_info.lamports(), accounts, &[&[ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - new_auction_key.as_ref(), + expected_auction_key.as_ref(), &[new_auction_custody_bump], ]], )?; - // TODO: Double-check that we do not need verify the derived pubkey is - // correct. We shouldn't have to because the seeds are used to transfer - // tokens, which will only work if the transfer authority pubkey is correct. - let (_, transfer_authority_bump) = Pubkey::find_program_address( + // We will use the expected transfer authority account key to invoke the + // SPL token transfer instruction. + let (expected_transfer_authority_key, transfer_authority_bump) = Pubkey::find_program_address( &[ TRANSFER_AUTHORITY_SEED_PREFIX, - new_auction_key.as_ref(), + expected_auction_key.as_ref(), &offer_price.to_be_bytes(), ], &ID, @@ -245,7 +263,7 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - &spl_token::ID, offer_token_info.key, new_auction_custody_info.key, - transfer_authority.key, + &expected_transfer_authority_key, &[], fast_market_order .amount_in @@ -259,36 +277,16 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - accounts, &[&[ TRANSFER_AUTHORITY_SEED_PREFIX, - new_auction_key.as_ref(), + expected_auction_key.as_ref(), &offer_price.to_be_bytes(), &[transfer_authority_bump], ]], )?; - let vaa_sequence = fast_market_order.vaa_sequence; - let consistency_level = fast_market_order.vaa_consistency_level; - - // Generate the VAA digest. This digest is used as the seed for the newly - // created auction account. - let vaa_message_digest = super::helpers::VaaMessageBodyHeader { - consistency_level, - timestamp: vaa_timestamp, - sequence: vaa_sequence, - emitter_chain: from_endpoint_account.chain, - emitter_address: from_endpoint_account.address, - } - .digest(&fast_market_order); - - // TODO: Double-check that we do not need verify the derived pubkey is - // correct. We shouldn't have to because the seeds are used to create the - // account, which will only work if the auction pubkey is correct. - let (_, new_auction_bump) = - Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_message_digest.0], &ID); - // Create the auction account and serialize its data into it. super::helpers::create_account_reliably( payer_info.key, - new_auction_key, + &expected_auction_key, new_auction_info.lamports(), 8 + Auction::INIT_SPACE, accounts, From 636aacbf88ee0fd5235c4803338499fdc4cdce53 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Mon, 12 May 2025 10:51:39 -0500 Subject: [PATCH 104/112] solana: clean up execute order Co-authored-by: Bengt Lofgren --- solana/programs/matching-engine/src/error.rs | 1 + .../src/fallback/processor/burn_and_post.rs | 29 +- .../src/fallback/processor/execute_order.rs | 723 ++++++++---------- .../fallback/processor/process_instruction.rs | 3 +- 4 files changed, 351 insertions(+), 405 deletions(-) diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index 6f1e4b83e..2a168bf3d 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -72,6 +72,7 @@ pub enum MatchingEngineError { AuctionExists = 0x428, NoAuction = 0x429, BestOfferTokenMismatch = 0x42a, + InitialOfferTokenMismatch = 0x42b, BestOfferTokenRequired = 0x42c, PreparedByMismatch = 0x42e, PreparedOrderResponseNotRequired = 0x42f, diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index ffa83ebd7..eda9a5884 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -15,15 +15,11 @@ use wormhole_svm_definitions::solana::{ use wormhole_svm_shim::post_message; // This is a helper struct to make it easier to pass in the accounts for the post_message instruction. -pub struct PostMessageAccounts { - pub emitter: Pubkey, - pub payer: Pubkey, - pub derived: PostMessageDerivedAccounts, -} - -pub struct PostMessageDerivedAccounts { - pub message: Pubkey, - pub sequence: Pubkey, +pub struct PostMessageAccounts<'ix> { + pub emitter: &'ix Pubkey, + pub payer: &'ix Pubkey, + pub message: &'ix Pubkey, + pub sequence: &'ix Pubkey, } pub fn burn_and_post<'info>( @@ -42,16 +38,23 @@ pub fn burn_and_post<'info>( payload, } = burn_and_publish_args; + let PostMessageAccounts { + emitter, + payer, + message, + sequence, + } = post_message_accounts; + // Post message to the shim program let post_message_ix = post_message::PostMessage { program_id: &POST_MESSAGE_SHIM_PROGRAM_ID, accounts: post_message::PostMessageAccounts { - emitter: &post_message_accounts.emitter, - payer: &post_message_accounts.payer, + emitter, + payer, wormhole_program_id: &CORE_BRIDGE_PROGRAM_ID, derived: post_message::PostMessageDerivedAccounts { - message: Some(&post_message_accounts.derived.message), - sequence: Some(&post_message_accounts.derived.sequence), + message: Some(&message), + sequence: Some(&sequence), core_bridge_config: Some(&CORE_BRIDGE_CONFIG), fee_collector: Some(&CORE_BRIDGE_FEE_COLLECTOR), event_authority: Some(&POST_MESSAGE_SHIM_EVENT_AUTHORITY), diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 2ef920307..2a3025b2b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -1,73 +1,80 @@ -use crate::fallback::burn_and_post::PostMessageDerivedAccounts; -use crate::fallback::helpers::*; -use crate::state::{ - Auction, AuctionConfig, AuctionStatus, Custodian, FastMarketOrder as FastMarketOrderState, - MessageProtocol, RouterEndpoint, -}; -use crate::utils::auction::DepositPenalty; -use crate::{utils, ID}; use anchor_lang::prelude::*; use anchor_spl::token::{spl_token, TokenAccount}; -use common::messages::Fill; -use common::wormhole_io::TypePrefixedPayload; -use solana_program::instruction::Instruction; -use solana_program::program::invoke_signed_unchecked; +use common::{ + messages::Fill, + wormhole_cctp_solana::cctp::token_messenger_minter_program::ID as CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID, + wormhole_io::TypePrefixedPayload, +}; +use solana_program::{instruction::Instruction, program::invoke_signed_unchecked}; + +use crate::{ + error::MatchingEngineError, + state::{Auction, AuctionStatus, Custodian, MessageProtocol}, + utils::{self, auction::DepositPenalty}, + ID, +}; use super::burn_and_post::{burn_and_post, PostMessageAccounts}; -use super::FallbackMatchingEngineInstruction; -use crate::error::MatchingEngineError; +// TODO: Rename to "ExecuteOrderCctpV2Accounts". #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExecuteOrderShimAccounts<'ix> { - /// The signer account + /// The signer account. + // TODO: Rename payer. pub signer: &'ix Pubkey, // 0 - /// The cctp message account. CHECK: Seeds must be \["cctp-msg", auction_address.as_ref()\]. + /// The cctp message account. Seeds must be \["cctp-msg", auction_address.as_ref()\]. + // TODO: Rename to "new_cctp_message". pub cctp_message: &'ix Pubkey, // 1 - /// The custodian account of the auction (holds the best offer amount) pub custodian: &'ix Pubkey, // 2 - /// The fast market order account created from the place initial offer instruction - /// CHECK: Seeds must be \["fast_market_order", auction_address.as_ref()\]. + /// Seeds must be \["fast_market_order", auction_address.as_ref()\]. pub fast_market_order: &'ix Pubkey, // 3 - /// The auction account created from the place initial offer instruction + /// The auction account created from the place initial offer instruction. pub active_auction: &'ix Pubkey, // 4 - /// The associated token address of the auction's custody token + /// The associated token address of the auction's custody token. + // TODO: Rename to "auction_custody". pub active_auction_custody_token: &'ix Pubkey, // 5 - /// The auction config account created from the place initial offer instruction + /// The auction config account created from the place initial offer instruction. + // TODO: Rename to "auction_config". pub active_auction_config: &'ix Pubkey, // 6 /// The token account of the auction's best offer + // TODO: Rename to "auction_best_offer_token". pub active_auction_best_offer_token: &'ix Pubkey, // 7 /// The token account of the executor pub executor_token: &'ix Pubkey, // 8 /// The token account of the auction's initial offer + // TODO: Rename to "auction_initial_offer_token". pub initial_offer_token: &'ix Pubkey, // 9 /// The account that signed the creation of the auction when placing the initial offer. + // TODO: Rename to "auction_initial_participant". pub initial_participant: &'ix Pubkey, // 10 /// The router endpoint account of the auction's target chain + // TODO: Rename to "to_endpoint". pub to_router_endpoint: &'ix Pubkey, // 11 /// The program id of the post message shim program pub post_message_shim_program: &'ix Pubkey, // 12 /// The emitter sequence of the core bridge program (can be derived) pub core_bridge_emitter_sequence: &'ix Pubkey, // 13 /// The message account of the post message shim program (can be derived) + // TODO: Rename to "shim_message". pub post_shim_message: &'ix Pubkey, // 14 + pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 15 /// The mint account of the CCTP token to be burned - pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 15 + pub cctp_deposit_for_burn_mint: &'ix Pubkey, // 16 /// The token messenger minter sender authority account of the CCTP token to be burned - pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 16 + pub cctp_deposit_for_burn_token_messenger_minter_sender_authority: &'ix Pubkey, // 17 /// The message transmitter config account of the CCTP token to be burned - pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 17 + pub cctp_deposit_for_burn_message_transmitter_config: &'ix Pubkey, // 18 /// The token messenger account of the CCTP token to be burned - pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 18 + pub cctp_deposit_for_burn_token_messenger: &'ix Pubkey, // 19 /// The remote token messenger account of the CCTP token to be burned - pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 19 + pub cctp_deposit_for_burn_remote_token_messenger: &'ix Pubkey, // 20 /// The token minter account of the CCTP token to be burned - pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 20 + pub cctp_deposit_for_burn_token_minter: &'ix Pubkey, // 21 /// The local token account of the CCTP token to be burned - pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 21 + pub cctp_deposit_for_burn_local_token: &'ix Pubkey, // 22 /// The token messenger minter event authority account of the CCTP token to be burned - pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 22 + pub cctp_deposit_for_burn_token_messenger_minter_event_authority: &'ix Pubkey, // 23 /// The token messenger minter program account of the CCTP token to be burned - pub cctp_deposit_for_burn_token_messenger_minter_program: &'ix Pubkey, // 23 /// The message transmitter program account of the CCTP token to be burned pub cctp_deposit_for_burn_message_transmitter_program: &'ix Pubkey, // 24 /// The program id of the core bridge program @@ -79,67 +86,17 @@ pub struct ExecuteOrderShimAccounts<'ix> { /// The event authority account of the post message shim program pub post_message_shim_event_authority: &'ix Pubkey, // 28 /// The program id of the system program + // TODO: Remove. pub system_program: &'ix Pubkey, // 29 /// The program id of the token program + // TODO: Remove. pub token_program: &'ix Pubkey, // 30 /// The clock account + // TODO: Remove. pub clock: &'ix Pubkey, // 31 } -impl<'ix> ExecuteOrderShimAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new(*self.signer, true), - AccountMeta::new(*self.cctp_message, false), - AccountMeta::new(*self.custodian, false), - AccountMeta::new_readonly(*self.fast_market_order, false), - AccountMeta::new(*self.active_auction, false), - AccountMeta::new(*self.active_auction_custody_token, false), - AccountMeta::new_readonly(*self.active_auction_config, false), - AccountMeta::new(*self.active_auction_best_offer_token, false), - AccountMeta::new(*self.executor_token, false), - AccountMeta::new(*self.initial_offer_token, false), - AccountMeta::new(*self.initial_participant, false), - AccountMeta::new_readonly(*self.to_router_endpoint, false), - AccountMeta::new_readonly(*self.post_message_shim_program, false), - AccountMeta::new(*self.core_bridge_emitter_sequence, false), - AccountMeta::new(*self.post_shim_message, false), - AccountMeta::new(*self.cctp_deposit_for_burn_mint, false), - AccountMeta::new_readonly( - *self.cctp_deposit_for_burn_token_messenger_minter_sender_authority, - false, - ), - AccountMeta::new( - *self.cctp_deposit_for_burn_message_transmitter_config, - false, - ), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_messenger, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_remote_token_messenger, false), - AccountMeta::new_readonly(*self.cctp_deposit_for_burn_token_minter, false), - AccountMeta::new(*self.cctp_deposit_for_burn_local_token, false), - AccountMeta::new_readonly( - *self.cctp_deposit_for_burn_token_messenger_minter_event_authority, - false, - ), - AccountMeta::new_readonly( - *self.cctp_deposit_for_burn_token_messenger_minter_program, - false, - ), - AccountMeta::new_readonly( - *self.cctp_deposit_for_burn_message_transmitter_program, - false, - ), - AccountMeta::new_readonly(*self.core_bridge_program, false), - AccountMeta::new(*self.core_bridge_config, false), - AccountMeta::new(*self.core_bridge_fee_collector, false), - AccountMeta::new(*self.post_message_shim_event_authority, false), - AccountMeta::new_readonly(*self.system_program, false), - AccountMeta::new_readonly(*self.token_program, false), - AccountMeta::new_readonly(*self.clock, false), - ] - } -} - +// TODO: Rename to "ExecuteOrderCctpV2". pub struct ExecuteOrderCctpShim<'ix> { pub program_id: &'ix Pubkey, pub accounts: ExecuteOrderShimAccounts<'ix>, @@ -147,286 +104,251 @@ pub struct ExecuteOrderCctpShim<'ix> { impl ExecuteOrderCctpShim<'_> { pub fn instruction(&self) -> Instruction { + let ExecuteOrderShimAccounts { + signer: payer, + cctp_message: new_cctp_message, + custodian, + fast_market_order, + active_auction, + active_auction_custody_token: auction_custody, + active_auction_config: auction_config, + active_auction_best_offer_token: auction_best_offer_token, + executor_token, + initial_offer_token: auction_initial_offer_token, + initial_participant: auction_initial_participant, + to_router_endpoint: to_endpoint, + post_message_shim_program, + core_bridge_emitter_sequence, + post_shim_message: shim_message, + cctp_deposit_for_burn_mint: cctp_mint, + cctp_deposit_for_burn_token_messenger_minter_sender_authority: + cctp_token_messenger_minter_sender_authority, + cctp_deposit_for_burn_message_transmitter_config: cctp_message_transmitter_config, + cctp_deposit_for_burn_token_messenger: cctp_token_messenger, + cctp_deposit_for_burn_remote_token_messenger: cctp_remote_token_messenger, + cctp_deposit_for_burn_token_minter: cctp_token_minter, + cctp_deposit_for_burn_local_token: cctp_local_token, + cctp_deposit_for_burn_token_messenger_minter_event_authority: + cctp_token_messenger_minter_event_authority, + cctp_deposit_for_burn_token_messenger_minter_program: + cctp_token_messenger_minter_program, + cctp_deposit_for_burn_message_transmitter_program: cctp_message_transmitter_program, + core_bridge_program, + core_bridge_config, + core_bridge_fee_collector, + post_message_shim_event_authority, + system_program: _, + token_program: _, + clock: _, + } = self.accounts; + Instruction { program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::ExecuteOrderCctpShim.to_vec(), + accounts: vec![ + AccountMeta::new(*payer, true), + AccountMeta::new(*new_cctp_message, false), + AccountMeta::new(*custodian, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new(*active_auction, false), + AccountMeta::new(*auction_custody, false), + AccountMeta::new_readonly(*auction_config, false), + AccountMeta::new(*auction_best_offer_token, false), + AccountMeta::new(*executor_token, false), + AccountMeta::new(*auction_initial_offer_token, false), + AccountMeta::new(*auction_initial_participant, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new_readonly(*post_message_shim_program, false), + AccountMeta::new(*core_bridge_emitter_sequence, false), + AccountMeta::new(*shim_message, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), + AccountMeta::new(*cctp_mint, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), + AccountMeta::new(*cctp_message_transmitter_config, false), + AccountMeta::new_readonly(*cctp_token_messenger, false), + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), + AccountMeta::new_readonly(*cctp_token_minter, false), + AccountMeta::new(*cctp_local_token, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*core_bridge_program, false), + AccountMeta::new(*core_bridge_config, false), + AccountMeta::new(*core_bridge_fee_collector, false), + AccountMeta::new(*post_message_shim_event_authority, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(solana_program::sysvar::clock::ID, false), + ], + data: super::FallbackMatchingEngineInstruction::ExecuteOrderCctpShim.to_vec(), } } } -pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { +pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not - require_min_account_infos_len(accounts, 31)?; + super::helpers::require_min_account_infos_len(accounts, 31)?; // Get the accounts - let signer_account = &accounts[0]; - let cctp_message_account = &accounts[1]; - let custodian_account = &accounts[2]; - let fast_market_order_account = &accounts[3]; - let active_auction_account = &accounts[4]; - let active_auction_custody_token_account = &accounts[5]; - let active_auction_config_account = &accounts[6]; - let active_auction_best_offer_token_account = &accounts[7]; - let executor_token_account = &accounts[8]; - let initial_offer_token_account = &accounts[9]; - let initial_participant_account = &accounts[10]; - let to_router_endpoint_account = &accounts[11]; - let _post_message_shim_program_account = &accounts[12]; - let core_bridge_emitter_sequence_account = &accounts[13]; - let post_shim_message_account = &accounts[14]; - let cctp_deposit_for_burn_mint_account = &accounts[15]; - let cctp_deposit_for_burn_token_messenger_minter_sender_authority_account = &accounts[16]; - let cctp_deposit_for_burn_message_transmitter_config_account = &accounts[17]; - let cctp_deposit_for_burn_token_messenger_account = &accounts[18]; - let cctp_deposit_for_burn_remote_token_messenger_account = &accounts[19]; - let cctp_deposit_for_burn_token_minter_account = &accounts[20]; - let cctp_deposit_for_burn_local_token_account = &accounts[21]; - let cctp_deposit_for_burn_token_messenger_minter_event_authority_account = &accounts[22]; - let cctp_deposit_for_burn_token_messenger_minter_program_account = &accounts[23]; - let cctp_deposit_for_burn_message_transmitter_program_account = &accounts[24]; - let _core_bridge_program_account = &accounts[25]; - let _core_bridge_config_account = &accounts[26]; - let _core_bridge_fee_collector_account = &accounts[27]; - let _post_message_shim_event_authority_account = &accounts[28]; - let system_program_account = &accounts[29]; - let token_program_account = &accounts[30]; - - // Do checks - // ------------------------------------------------------------------------------------------------ - - let fast_market_order_zero_copy = - super::helpers::try_fast_market_order_account(fast_market_order_account)?; + let payer_info = &accounts[0]; + let new_cctp_message_info = &accounts[1]; - // Bind value for compiler (needed for pda seeds) - let active_auction_key = active_auction_account.key(); - - // Check cctp message is mutable - if !cctp_message_account.is_writable { - msg!("Cctp message is not writable"); - return Err(MatchingEngineError::AccountNotWritable.into()) - .map_err(|e: Error| e.with_account_name("cctp_message")); - } - - // Check cctp message seeds - let cctp_message_seeds = [ - common::CCTP_MESSAGE_SEED_PREFIX, - active_auction_key.as_ref(), - ]; - - let (cctp_message_pda, cctp_message_bump) = - Pubkey::find_program_address(&cctp_message_seeds, &ID); - if cctp_message_pda != cctp_message_account.key() { - msg!("Cctp message seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((cctp_message_pda, cctp_message_account.key()))); - }; - - // Check custodian owner - super::helpers::require_owned_by_this_program(custodian_account, "custodian")?; - - // Check custodian deserialises into a checked custodian account - let _checked_custodian = Custodian::try_deserialize(&mut &custodian_account.data.borrow()[..])?; - - let fast_market_order_digest = fast_market_order_zero_copy.digest(); - // Check fast market order seeds - let fast_market_order_seeds = [ - FastMarketOrderState::SEED_PREFIX, - fast_market_order_digest.as_ref(), - fast_market_order_zero_copy - .close_account_refund_recipient - .as_ref(), - ]; - - let (fast_market_order_pda, _fast_market_order_bump) = - Pubkey::find_program_address(&fast_market_order_seeds, &ID); - if fast_market_order_pda != fast_market_order_account.key() { - msg!("Fast market order seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys((fast_market_order_pda, fast_market_order_account.key())) - }); - }; + let custodian_info = &accounts[2]; + super::helpers::try_custodian_account( + custodian_info, + false, // check_if_paused + )?; - // Check fast market order is owned by the matching engine program - if fast_market_order_account.owner != &ID { - msg!("Fast market order is not owned by the matching engine program"); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("fast_market_order")); - }; + let fast_market_order = super::helpers::try_fast_market_order_account(&accounts[3])?; - // Check active auction owner - if active_auction_account.owner != &ID { - msg!("Active auction is not owned by the matching engine program"); - return Err(ErrorCode::ConstraintOwner.into()) - .map_err(|e: Error| e.with_account_name("active_auction")); - }; + let active_auction_info = &accounts[4]; + super::helpers::require_owned_by_this_program(active_auction_info, "active_auction")?; - // Check active auction pda - let mut active_auction = - Auction::try_deserialize(&mut &active_auction_account.data.borrow()[..])?; + let active_auction_key = active_auction_info.key(); + let mut active_auction = Auction::try_deserialize(&mut &active_auction_info.data.borrow()[..])?; + let active_auction_inner_info = active_auction.info.as_ref().unwrap(); require!( - fast_market_order_digest == active_auction.vaa_hash, + active_auction.vaa_hash == fast_market_order.digest(), MatchingEngineError::VaaMismatch ); - // Correct way to use create_program_address with existing seeds and bump - let active_auction_pda = Pubkey::create_program_address( - &[ - Auction::SEED_PREFIX, - active_auction.vaa_hash.as_ref(), - &[active_auction.bump], - ], - &ID, - ) - .map_err(|_| { - msg!("Failed to create program address with known bump"); - MatchingEngineError::InvalidPda - })?; - if active_auction_pda != active_auction_account.key() { - msg!("Active auction pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys((active_auction_pda, active_auction_account.key())) - }); - }; + require!( + active_auction.status == AuctionStatus::Active, + MatchingEngineError::AuctionNotActive + ); - // Check active auction is active - if active_auction.status != AuctionStatus::Active { - msg!("Active auction is not active"); - return Err(ErrorCode::ConstraintRaw.into()) - .map_err(|e: Error| e.with_account_name("active_auction")); - }; + let auction_custody_info = &accounts[5]; // Check active auction custody token pda - let active_auction_custody_token_pda = Pubkey::create_program_address( + match Pubkey::create_program_address( &[ crate::AUCTION_CUSTODY_TOKEN_SEED_PREFIX, - active_auction_account.key().as_ref(), - &[active_auction.info.as_ref().unwrap().custody_token_bump], + active_auction_key.as_ref(), + &[active_auction_inner_info.custody_token_bump], ], &ID, - ) - .map_err(|_| { - msg!("Failed to create program address with known bump"); - MatchingEngineError::InvalidPda - })?; - if active_auction_custody_token_pda != active_auction_custody_token_account.key() { - msg!("Active auction custody token pda is invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys(( - active_auction_custody_token_pda, - active_auction_custody_token_account.key(), - )) - }); + ) { + Err(_) => { + return Err(MatchingEngineError::InvalidPda.into()) + .map_err(|e: Error| e.with_account_name("auction_custody")) + } + Ok(expected_key) if auction_custody_info.key != &expected_key => { + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_account_name("auction_custody") + .with_pubkeys((*auction_custody_info.key, expected_key)) + }) + } + _ => (), }; - // Check active auction config id - let active_auction_config = - AuctionConfig::try_deserialize(&mut &active_auction_config_account.data.borrow()[..])?; - if active_auction_config.id != active_auction.info.as_ref().unwrap().config_id { - msg!("Active auction config id is invalid"); - return Err(MatchingEngineError::AuctionConfigMismatch.into()) - .map_err(|e: Error| e.with_account_name("active_auction_config")); - }; + // It is safe to unwrap here because we know the auction status is active, + // which means its inner info is some `AuctionInfo`. This info specifies + // which config ID was used. + // + // This inner info will also be used for token transfer accounting. + let auction_config = super::helpers::try_auction_config_account( + &accounts[6], + Some(active_auction_inner_info.config_id), + )?; - // Check that the auction has reached its deadline - let auction_info = active_auction.info.as_ref().unwrap(); - if auction_info.within_auction_duration(&active_auction_config.parameters) { - msg!("Auction has not reached its deadline"); - return Err(MatchingEngineError::AuctionPeriodNotExpired.into()) - .map_err(|e: Error| e.with_account_name("active_auction")); - } + // If solvers can still participate in the auction, we disallow executing + // this auction's fast order. + require!( + !active_auction_inner_info.within_auction_duration(&auction_config), + MatchingEngineError::AuctionPeriodNotExpired + ); - // Check active auction best offer token address - if active_auction_best_offer_token_account.key() - != active_auction.info.as_ref().unwrap().best_offer_token - { - msg!("Active auction best offer token address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - active_auction_best_offer_token_account.key(), - active_auction.info.as_ref().unwrap().best_offer_token, - )) - }); - }; + let auction_best_offer_token_info = &accounts[7]; - // Check initial offer token address - if initial_offer_token_account.key() - != active_auction.info.as_ref().unwrap().initial_offer_token - { - msg!("Initial offer token address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - initial_offer_token_account.key(), - active_auction.info.as_ref().unwrap().initial_offer_token, - )) - }); - }; + require_keys_eq!( + *auction_best_offer_token_info.key, + active_auction_inner_info.best_offer_token, + MatchingEngineError::BestOfferTokenMismatch + ); + + let executor_token_info = &accounts[8]; + let auction_initial_offer_token_info = &accounts[9]; + + require_keys_eq!( + *auction_initial_offer_token_info.key, + active_auction_inner_info.initial_offer_token, + MatchingEngineError::InitialOfferTokenMismatch + ); + + let auction_initial_participant_info = &accounts[10]; - // Check initial participant address - if initial_participant_account.key() != active_auction.prepared_by { - msg!("Initial participant address is invalid"); + if auction_initial_participant_info.key != &active_auction.prepared_by { return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - initial_participant_account.key(), + e.with_account_name("initial_participant").with_pubkeys(( + *auction_initial_participant_info.key, active_auction.prepared_by, )) }); }; - let to_router_endpoint = - RouterEndpoint::try_deserialize(&mut &to_router_endpoint_account.data.borrow()[..])?; - if to_router_endpoint.protocol != active_auction.target_protocol { - msg!("To router endpoint protocol is invalid"); - return Err(MatchingEngineError::InvalidEndpoint.into()) - .map_err(|e: Error| e.with_account_name("to_router_endpoint")); - }; + let to_endpoint = super::helpers::try_live_endpoint_account(&accounts[11], "to_endpoint")?; - let destination_cctp_domain = match to_router_endpoint.protocol { + // We ensure that the destination endpoint account is what we expect given + // the target protocol found in the active auction account data. + require_eq!( + to_endpoint.protocol, + active_auction.target_protocol, + MatchingEngineError::InvalidTargetRouter + ); + + // This CCTP domain will be used later in the instruction to invoke CCTP + // deposit for burn. But we assign this value here so we can revert early + // based on which kind of message protocol the registered destination + // endpoint is. + let destination_cctp_domain = match to_endpoint.protocol { MessageProtocol::Cctp { domain } => domain, _ => { return Err(MatchingEngineError::InvalidCctpEndpoint.into()) - .map_err(|e: Error| e.with_account_name("to_router_endpoint")) + .map_err(|e: Error| e.with_account_name("to_endpoint")) } }; - // Check cctp deposit for burn token messenger minter program address - if cctp_deposit_for_burn_token_messenger_minter_program_account.key() - != common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id() - { - msg!("Cctp deposit for burn token messenger minter program address is invalid"); - return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - cctp_deposit_for_burn_token_messenger_minter_program_account.key(), - common::wormhole_cctp_solana::cctp::token_messenger_minter_program::id(), - )) - }); - }; + // TODO: Consider grouping with the wormhole shim account infos? + let _post_message_shim_program_info = &accounts[12]; - // Check cctp deposit for burn message transmitter program address - if cctp_deposit_for_burn_message_transmitter_program_account.key() - != common::wormhole_cctp_solana::cctp::message_transmitter_program::id() - { - msg!("Cctp deposit for burn message transmitter program address is invalid"); + let core_bridge_emitter_sequence_info = &accounts[13]; + let shim_message_info = &accounts[14]; + + // These accounts will be used to invoke the CCTP Token Messenger Minter + // program to burn USDC (to be minted at the destination network). + let cctp_account_infos = &accounts[16..25]; + + // These accounts do not actually have to be in any particular order even if + // an updated Anchor IDL specifies an order. + let _wormhole_shim_account_infos = &accounts[25..28]; + + // Do checks + // ------------------------------------------------------------------------------------------------ + + let cctp_token_messenger_minter_program_info = &accounts[15]; + + // Check cctp deposit for burn token messenger minter program address + if cctp_token_messenger_minter_program_info.key != &CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID { return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { - e.with_pubkeys(( - cctp_deposit_for_burn_message_transmitter_program_account.key(), - common::wormhole_cctp_solana::cctp::message_transmitter_program::id(), - )) + e.with_account_name("token_messenger_minter_program") + .with_pubkeys(( + *cctp_token_messenger_minter_program_info.key, + CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID, + )) }); }; - // End of checks - // ------------------------------------------------------------------------------------------------ + // TODO: Do we have to verify the CCTP message transmitter program is passed + // in? - // Get the fast market order data, without the discriminator - let fast_market_order_data = &fast_market_order_account.data.borrow()[8..]; - // Deserialise fast market order. Unwrap is safe because the account is owned by the matching engine program. - let fast_market_order = - bytemuck::try_from_bytes::(fast_market_order_data).unwrap(); + //////////////////////////////////////////////////////////////////////////// + // + // TODO: This execute order logic has been taken from the original execute + // order instructions. We plan on using a helper method instead of copy- + // pasting the same logic here. + // + //////////////////////////////////////////////////////////////////////////// // Prepare the execute order (get the user amount, fill, and order executed event) - let active_auction_info = active_auction.info.as_ref().unwrap(); let current_slot = Clock::get().unwrap().slot; // We extend the grace period for locally executed orders. Reserving a sequence number for @@ -444,37 +366,36 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { penalty, user_reward, } = utils::auction::compute_deposit_penalty( - &active_auction_config.parameters, - active_auction_info, + &auction_config, + active_auction_inner_info, current_slot, additional_grace_period, ); let init_auction_fee = fast_market_order.init_auction_fee; - let user_amount = active_auction_info + let user_amount = active_auction_inner_info .amount_in - .saturating_sub(active_auction_info.offer_price) + .saturating_sub(active_auction_inner_info.offer_price) .saturating_sub(init_auction_fee) .saturating_add(user_reward); // Keep track of the remaining amount in the custody token account. Whatever remains will go // to the executor. - let custody_token = TokenAccount::try_deserialize( - &mut &active_auction_custody_token_account.data.borrow()[..], - )?; + let custody_token = + TokenAccount::try_deserialize(&mut &auction_custody_info.data.borrow()[..])?; let mut remaining_custodied_amount = custody_token.amount.saturating_sub(user_amount); // Offer price + security deposit was checked in placing the initial offer. - let mut deposit_and_fee = active_auction_info + let mut deposit_and_fee = active_auction_inner_info .offer_price - .saturating_add(active_auction_info.security_deposit) + .saturating_add(active_auction_inner_info.security_deposit) .saturating_sub(user_reward); let penalized = penalty > 0; - if penalized && active_auction_best_offer_token_account.key() != executor_token_account.key() { + if penalized && auction_best_offer_token_info.key != executor_token_info.key { deposit_and_fee = deposit_and_fee.saturating_sub(penalty); } @@ -489,16 +410,19 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // init auction fee. The executor will get these funds instead. // // We check that this is a legitimate token account. - if utils::checked_deserialize_token_account(initial_offer_token_account, &common::USDC_MINT) - .is_some() + if utils::checked_deserialize_token_account( + auction_initial_offer_token_info, + &common::USDC_MINT, + ) + .is_some() { - if active_auction_best_offer_token_account.key() != initial_offer_token_account.key() { + if auction_best_offer_token_info.key() != auction_initial_offer_token_info.key() { // Pay the auction initiator their fee. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, - &active_auction_custody_token_account.key(), - &initial_offer_token_account.key(), - &active_auction_account.key(), + &auction_custody_info.key(), + &auction_initial_offer_token_info.key(), + &active_auction_info.key(), &[], init_auction_fee, ) @@ -517,7 +441,7 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { } // Return the security deposit and the fee to the highest bidder. - if active_auction_best_offer_token_account.key() == executor_token_account.key() { + if auction_best_offer_token_info.key == executor_token_info.key { // If the best offer token is equal to the executor token, just send whatever remains in // the custody token account. // @@ -526,9 +450,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // offer token would have received. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, - &active_auction_custody_token_account.key(), - &active_auction_best_offer_token_account.key(), - &active_auction_account.key(), + &auction_custody_info.key(), + &auction_best_offer_token_info.key(), + &active_auction_info.key(), &[], deposit_and_fee, ) @@ -543,16 +467,16 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // doesn't exist at this point (which would be unusual), we will reserve these funds // for the executor token. if utils::checked_deserialize_token_account( - active_auction_best_offer_token_account, + auction_best_offer_token_info, &common::USDC_MINT, ) .is_some() { let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, - &active_auction_custody_token_account.key(), - &active_auction_best_offer_token_account.key(), - &active_auction_account.key(), + &auction_custody_info.key(), + &auction_best_offer_token_info.key(), + &active_auction_info.key(), &[], deposit_and_fee, ) @@ -569,9 +493,9 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { if remaining_custodied_amount > 0 { let instruction = spl_token::instruction::transfer( &spl_token::ID, - &active_auction_custody_token_account.key(), - &executor_token_account.key(), - &active_auction_account.key(), + auction_custody_info.key, + executor_token_info.key, + &active_auction_info.key(), &[], remaining_custodied_amount, ) @@ -588,10 +512,10 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { // here. let set_authority_ix = spl_token::instruction::set_authority( &spl_token::ID, - &active_auction_custody_token_account.key(), - Some(&custodian_account.key()), + auction_custody_info.key, + Some(custodian_info.key), spl_token::instruction::AuthorityType::AccountOwner, - &active_auction_account.key(), + active_auction_info.key, &[], ) .unwrap(); @@ -604,12 +528,12 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { execute_penalty: if penalized { penalty.into() } else { None }, }; - let active_auction_data: &mut [u8] = &mut active_auction_account.data.borrow_mut(); - let mut cursor = std::io::Cursor::new(active_auction_data); - active_auction.try_serialize(&mut cursor).unwrap(); + let active_auction_info_data: &mut [u8] = &mut active_auction_info.data.borrow_mut(); + let mut active_auction_cursor = std::io::Cursor::new(active_auction_info_data); + active_auction.try_serialize(&mut active_auction_cursor)?; let fill = Fill { - source_chain: active_auction_info.source_chain, + source_chain: active_auction_inner_info.source_chain, order_sender: fast_market_order.sender, redeemer: fast_market_order.redeemer, redeemer_message: fast_market_order.redeemer_message @@ -619,79 +543,98 @@ pub fn handle_execute_order_shim(accounts: &[AccountInfo]) -> Result<()> { .unwrap(), }; - let post_message_accounts = PostMessageAccounts { - emitter: custodian_account.key(), - payer: signer_account.key(), - derived: PostMessageDerivedAccounts { - message: post_shim_message_account.key(), - sequence: core_bridge_emitter_sequence_account.key(), - }, - }; + //////////////////////////////////////////////////////////////////////////// + // + // TODO: See above TODO. This is the end of the copy-pasted logic. + // + //////////////////////////////////////////////////////////////////////////// + + // TODO: Write test that passes in random keypair for CCTP message account + // to show that not having to check the PDA address is safe. + let (_, new_cctp_message_bump) = Pubkey::find_program_address( + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + active_auction_key.as_ref(), + ], + &ID, + ); + + let usdc_mint_info = super::helpers::try_usdc_account(&cctp_account_infos[0])?; + let cctp_token_messenger_minter_sender_authority_info = &cctp_account_infos[1]; + let cctp_message_transmitter_config_info = &cctp_account_infos[2]; + let cctp_token_messenger_info = &cctp_account_infos[3]; + let cctp_remote_token_messenger_info = &cctp_account_infos[4]; + let cctp_token_minter_info = &cctp_account_infos[5]; + let cctp_local_token_info = &cctp_account_infos[6]; + let cctp_token_messenger_minter_event_authority_info = &cctp_account_infos[7]; + let cctp_message_transmitter_program_info = &cctp_account_infos[8]; + + let system_program_info = &accounts[29]; + let token_program_info = &accounts[30]; burn_and_post( CpiContext::new_with_signer( - cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), + cctp_token_messenger_minter_program_info.to_account_info(), common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { - burn_token_owner: custodian_account.to_account_info(), - payer: signer_account.to_account_info(), + burn_token_owner: custodian_info.to_account_info(), + payer: payer_info.to_account_info(), token_messenger_minter_sender_authority: - cctp_deposit_for_burn_token_messenger_minter_sender_authority_account - .to_account_info(), - burn_token: active_auction_custody_token_account.to_account_info(), - message_transmitter_config: - cctp_deposit_for_burn_message_transmitter_config_account.to_account_info(), - token_messenger: cctp_deposit_for_burn_token_messenger_account.to_account_info(), - remote_token_messenger: cctp_deposit_for_burn_remote_token_messenger_account + cctp_token_messenger_minter_sender_authority_info.to_account_info(), + burn_token: auction_custody_info.to_account_info(), + message_transmitter_config: cctp_message_transmitter_config_info.to_account_info(), + token_messenger: cctp_token_messenger_info.to_account_info(), + remote_token_messenger: cctp_remote_token_messenger_info.to_account_info(), + token_minter: cctp_token_minter_info.to_account_info(), + local_token: cctp_local_token_info.to_account_info(), + mint: usdc_mint_info.to_account_info(), + cctp_message: new_cctp_message_info.to_account_info(), + message_transmitter_program: cctp_message_transmitter_program_info .to_account_info(), - token_minter: cctp_deposit_for_burn_token_minter_account.to_account_info(), - local_token: cctp_deposit_for_burn_local_token_account.to_account_info(), - mint: cctp_deposit_for_burn_mint_account.to_account_info(), - cctp_message: cctp_message_account.to_account_info(), - message_transmitter_program: - cctp_deposit_for_burn_message_transmitter_program_account.to_account_info(), - token_messenger_minter_program: - cctp_deposit_for_burn_token_messenger_minter_program_account.to_account_info(), - token_program: token_program_account.to_account_info(), - system_program: system_program_account.to_account_info(), - event_authority: - cctp_deposit_for_burn_token_messenger_minter_event_authority_account - .to_account_info(), + token_messenger_minter_program: cctp_token_messenger_minter_program_info + .to_account_info(), + token_program: token_program_info.to_account_info(), + system_program: system_program_info.to_account_info(), + event_authority: cctp_token_messenger_minter_event_authority_info.to_account_info(), }, &[ Custodian::SIGNER_SEEDS, &[ common::CCTP_MESSAGE_SEED_PREFIX, - active_auction_account.key().as_ref(), - &[cctp_message_bump], + active_auction_key.as_ref(), + &[new_cctp_message_bump], ], ], ), common::wormhole_cctp_solana::cpi::BurnAndPublishArgs { burn_source: None, - destination_caller: to_router_endpoint.address, + destination_caller: to_endpoint.address, destination_cctp_domain, amount: user_amount, - mint_recipient: to_router_endpoint.mint_recipient, + mint_recipient: to_endpoint.mint_recipient, wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, payload: fill.to_vec(), }, - post_message_accounts, + PostMessageAccounts { + emitter: custodian_info.key, + payer: payer_info.key, + message: shim_message_info.key, + sequence: core_bridge_emitter_sequence_info.key, + }, accounts, )?; // Skip emitting the order executed event because we're using a shim // Finally close the account since it is no longer needed. - let instruction = spl_token::instruction::close_account( + let close_account_ix = spl_token::instruction::close_account( &spl_token::ID, - &active_auction_custody_token_account.key(), - &initial_participant_account.key(), - &custodian_account.key(), + auction_custody_info.key, + auction_initial_participant_info.key, + custodian_info.key, &[], ) .unwrap(); - invoke_signed_unchecked(&instruction, accounts, &[Custodian::SIGNER_SEEDS])?; - - Ok(()) + invoke_signed_unchecked(&close_account_ix, accounts, &[Custodian::SIGNER_SEEDS]) + .map_err(Into::into) } diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index b5c74482c..df56f0366 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -3,7 +3,6 @@ use wormhole_svm_definitions::make_anchor_discriminator; use crate::ID; -use super::execute_order::handle_execute_order_shim; use super::initialize_fast_market_order::InitializeFastMarketOrderData; use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::prepare_order_response::{ @@ -57,7 +56,7 @@ pub fn process_instruction( super::place_initial_offer::process(accounts, data) } FallbackMatchingEngineInstruction::ExecuteOrderCctpShim => { - handle_execute_order_shim(accounts) + super::execute_order::process(accounts) } FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { prepare_order_response_cctp_shim(accounts, data) From dcd7e952fafa0d1615bb50217f18083869cbba15 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Tue, 13 May 2025 08:55:36 -0500 Subject: [PATCH 105/112] solana: clean up prepare order response --- .../processor/prepare_order_response.rs | 744 ++++++++---------- .../fallback/processor/process_instruction.rs | 10 +- 2 files changed, 338 insertions(+), 416 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index dd9ccc8b0..aba9487f7 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -1,82 +1,45 @@ -use std::io::Cursor; - -use super::helpers::{create_account_reliably, VaaMessageBodyHeader}; -use super::FallbackMatchingEngineInstruction; -use crate::fallback::helpers::create_usdc_token_account_reliably; -use crate::fallback::helpers::require_min_account_infos_len; -use crate::state::PreparedOrderResponseInfo; -use crate::state::PreparedOrderResponseSeeds; -use crate::state::{Custodian, MessageProtocol, PreparedOrderResponse, RouterEndpoint}; -use crate::CCTP_MINT_RECIPIENT; -use crate::ID; use anchor_lang::prelude::*; use anchor_spl::token::spl_token; -use common::messages::SlowOrderResponse; -use common::wormhole_cctp_solana::cctp::message_transmitter_program; -use common::wormhole_cctp_solana::cpi::ReceiveMessageArgs; -use common::wormhole_cctp_solana::messages::Deposit; -use common::wormhole_cctp_solana::utils::CctpMessage; -use common::wormhole_io::TypePrefixedPayload; +use borsh::{BorshDeserialize, BorshSerialize}; +use common::{ + messages::SlowOrderResponse, + wormhole_cctp_solana::{ + cctp::message_transmitter_program::{self, ID as CCTP_MESSAGE_TRANSMITTER_PROGRAM_ID}, + cpi::ReceiveMessageArgs, + messages::Deposit, + utils::CctpMessage, + }, + wormhole_io::TypePrefixedPayload, + USDC_MINT, +}; use ruint::aliases::U256; -use solana_program::instruction::Instruction; -use solana_program::keccak; -use solana_program::program::invoke_signed_unchecked; -use wormhole_io::WriteableBytes; - -use crate::error::MatchingEngineError; - -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +use solana_program::{instruction::Instruction, keccak, program::invoke_signed_unchecked}; +use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashAccounts, VerifyHashData}; + +use crate::{ + error::MatchingEngineError, + fallback::helpers::{create_usdc_token_account_reliably, require_min_account_infos_len}, + state::{ + Custodian, MessageProtocol, PreparedOrderResponse, PreparedOrderResponseInfo, + PreparedOrderResponseSeeds, + }, + CCTP_MINT_RECIPIENT, ID, +}; + +#[derive(BorshDeserialize, BorshSerialize)] pub struct PrepareOrderResponseCctpShimData { pub encoded_cctp_message: Vec, pub cctp_attestation: Vec, pub finalized_vaa_message_args: FinalizedVaaMessageArgs, } -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +#[derive(BorshDeserialize, BorshSerialize)] pub struct FinalizedVaaMessageArgs { pub base_fee: u64, // Can also get from deposit payload pub consistency_level: u8, pub guardian_set_bump: u8, } -impl FinalizedVaaMessageArgs { - // TODO: Change return type to keccak::Hash - pub fn digest( - &self, - vaa_message_body_header: VaaMessageBodyHeader, - deposit_vaa_payload: Deposit, - ) -> [u8; 32] { - let message_hash = keccak::hashv(&[ - vaa_message_body_header.timestamp.to_be_bytes().as_ref(), - [0, 0, 0, 0].as_ref(), // 0 nonce - vaa_message_body_header.emitter_chain.to_be_bytes().as_ref(), - &vaa_message_body_header.emitter_address, - &vaa_message_body_header.sequence.to_be_bytes(), - &[vaa_message_body_header.consistency_level], - deposit_vaa_payload.to_vec().as_ref(), - ]); - // Digest is the hash of the message - keccak::hashv(&[message_hash.as_ref()]).0 - } -} - -impl PrepareOrderResponseCctpShimData { - pub fn from_bytes(data: &[u8]) -> Option { - Self::try_from_slice(data).ok() - } - - pub fn to_receive_message_args(&self) -> ReceiveMessageArgs { - let mut encoded_message = Vec::with_capacity(self.encoded_cctp_message.len()); - encoded_message.extend_from_slice(&self.encoded_cctp_message); - let mut cctp_attestation = Vec::with_capacity(self.cctp_attestation.len()); - cctp_attestation.extend_from_slice(&self.cctp_attestation); - ReceiveMessageArgs { - encoded_message, - attestation: cctp_attestation, - } - } -} - pub struct PrepareOrderResponseCctpShimAccounts<'ix> { pub signer: &'ix Pubkey, // 0 pub custodian: &'ix Pubkey, // 1 @@ -87,60 +50,27 @@ pub struct PrepareOrderResponseCctpShimAccounts<'ix> { pub prepared_custody_token: &'ix Pubkey, // 6 pub base_fee_token: &'ix Pubkey, // 7 pub usdc: &'ix Pubkey, // 8 - pub cctp_mint_recipient: &'ix Pubkey, // 9 - pub cctp_message_transmitter_authority: &'ix Pubkey, // 10 - pub cctp_message_transmitter_config: &'ix Pubkey, // 11 - pub cctp_used_nonces: &'ix Pubkey, // 12 - pub cctp_message_transmitter_event_authority: &'ix Pubkey, // 13 - pub cctp_token_messenger: &'ix Pubkey, // 14 - pub cctp_remote_token_messenger: &'ix Pubkey, // 15 - pub cctp_token_minter: &'ix Pubkey, // 16 - pub cctp_local_token: &'ix Pubkey, // 17 - pub cctp_token_pair: &'ix Pubkey, // 18 - pub cctp_token_messenger_minter_custody_token: &'ix Pubkey, // 19 - pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, // 20 - pub cctp_token_messenger_minter_program: &'ix Pubkey, // 21 - pub cctp_message_transmitter_program: &'ix Pubkey, // 22 - pub guardian_set: &'ix Pubkey, // 23 - pub guardian_set_signatures: &'ix Pubkey, // 24 - pub verify_shim_program: &'ix Pubkey, // 25 - pub token_program: &'ix Pubkey, // 26 - pub system_program: &'ix Pubkey, // 27 -} - -impl<'ix> PrepareOrderResponseCctpShimAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new(*self.signer, true), - AccountMeta::new_readonly(*self.custodian, false), - AccountMeta::new_readonly(*self.fast_market_order, false), - AccountMeta::new_readonly(*self.from_endpoint, false), - AccountMeta::new_readonly(*self.to_endpoint, false), - AccountMeta::new(*self.prepared_order_response, false), - AccountMeta::new(*self.prepared_custody_token, false), - AccountMeta::new_readonly(*self.base_fee_token, false), - AccountMeta::new_readonly(*self.usdc, false), - AccountMeta::new(*self.cctp_mint_recipient, false), - AccountMeta::new_readonly(*self.cctp_message_transmitter_authority, false), - AccountMeta::new_readonly(*self.cctp_message_transmitter_config, false), - AccountMeta::new(*self.cctp_used_nonces, false), - AccountMeta::new_readonly(*self.cctp_message_transmitter_event_authority, false), - AccountMeta::new_readonly(*self.cctp_token_messenger, false), - AccountMeta::new_readonly(*self.cctp_remote_token_messenger, false), - AccountMeta::new_readonly(*self.cctp_token_minter, false), - AccountMeta::new(*self.cctp_local_token, false), - AccountMeta::new_readonly(*self.cctp_token_pair, false), - AccountMeta::new(*self.cctp_token_messenger_minter_custody_token, false), - AccountMeta::new_readonly(*self.cctp_token_messenger_minter_event_authority, false), - AccountMeta::new_readonly(*self.cctp_token_messenger_minter_program, false), - AccountMeta::new_readonly(*self.cctp_message_transmitter_program, false), - AccountMeta::new_readonly(*self.guardian_set, false), - AccountMeta::new_readonly(*self.guardian_set_signatures, false), - AccountMeta::new_readonly(*self.verify_shim_program, false), - AccountMeta::new_readonly(*self.token_program, false), - AccountMeta::new_readonly(*self.system_program, false), - ] - } + pub verify_shim_program: &'ix Pubkey, // 9 + pub guardian_set: &'ix Pubkey, // 10 + pub guardian_set_signatures: &'ix Pubkey, // 11 + pub cctp_message_transmitter_program: &'ix Pubkey, // 12 + pub cctp_message_transmitter_authority: &'ix Pubkey, // 13 + pub cctp_message_transmitter_config: &'ix Pubkey, // 14 + pub cctp_used_nonces: &'ix Pubkey, // 15 + pub cctp_message_transmitter_event_authority: &'ix Pubkey, // 16 + pub cctp_token_messenger: &'ix Pubkey, // 17 + pub cctp_remote_token_messenger: &'ix Pubkey, // 18 + pub cctp_token_minter: &'ix Pubkey, // 19 + pub cctp_local_token: &'ix Pubkey, // 20 + pub cctp_token_pair: &'ix Pubkey, // 21 + pub cctp_token_messenger_minter_custody_token: &'ix Pubkey, // 22 + pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, // 23 + pub cctp_token_messenger_minter_program: &'ix Pubkey, // 24 + pub cctp_mint_recipient: &'ix Pubkey, // 25 + // TODO: Remove + pub token_program: &'ix Pubkey, // 26 + // TODO: Remove + pub system_program: &'ix Pubkey, // 27 } pub struct PrepareOrderResponseCctpShim<'ix> { @@ -151,10 +81,70 @@ pub struct PrepareOrderResponseCctpShim<'ix> { impl<'ix> PrepareOrderResponseCctpShim<'ix> { pub fn instruction(self) -> Instruction { + let PrepareOrderResponseCctpShimAccounts { + signer, + custodian, + fast_market_order, + from_endpoint, + to_endpoint, + prepared_order_response, + prepared_custody_token, + base_fee_token, + usdc, + verify_shim_program, + guardian_set, + guardian_set_signatures, + cctp_message_transmitter_program, + cctp_mint_recipient, + cctp_message_transmitter_authority, + cctp_message_transmitter_config, + cctp_used_nonces, + cctp_message_transmitter_event_authority, + cctp_token_messenger, + cctp_remote_token_messenger, + cctp_token_minter, + cctp_local_token, + cctp_token_pair, + cctp_token_messenger_minter_custody_token, + cctp_token_messenger_minter_event_authority, + cctp_token_messenger_minter_program, + token_program: _, + system_program: _, + } = self.accounts; + Instruction { program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(self.data) + accounts: vec![ + AccountMeta::new(*signer, true), + AccountMeta::new_readonly(*custodian, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new_readonly(*from_endpoint, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new(*prepared_order_response, false), + AccountMeta::new(*prepared_custody_token, false), + AccountMeta::new_readonly(*base_fee_token, false), + AccountMeta::new_readonly(*usdc, false), + AccountMeta::new_readonly(*verify_shim_program, false), + AccountMeta::new_readonly(*guardian_set, false), + AccountMeta::new_readonly(*guardian_set_signatures, false), + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*cctp_message_transmitter_authority, false), + AccountMeta::new_readonly(*cctp_message_transmitter_config, false), + AccountMeta::new(*cctp_used_nonces, false), + AccountMeta::new_readonly(*cctp_message_transmitter_event_authority, false), + AccountMeta::new_readonly(*cctp_token_messenger, false), + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), + AccountMeta::new_readonly(*cctp_token_minter, false), + AccountMeta::new(*cctp_local_token, false), + AccountMeta::new_readonly(*cctp_token_pair, false), + AccountMeta::new(*cctp_token_messenger_minter_custody_token, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), + AccountMeta::new(*cctp_mint_recipient, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + ], + data: super::FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(self.data) .to_vec(), } } @@ -164,354 +154,290 @@ pub fn prepare_order_response_cctp_shim( accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData, ) -> Result<()> { - let program_id = &ID; require_min_account_infos_len(accounts, 27)?; - let signer = &accounts[0]; - let custodian = &accounts[1]; - let fast_market_order = &accounts[2]; - let from_endpoint = &accounts[3]; - let to_endpoint = &accounts[4]; - let prepared_order_response = &accounts[5]; - let prepared_custody_token = &accounts[6]; - let base_fee_token = &accounts[7]; - let usdc = &accounts[8]; - let cctp_mint_recipient = &accounts[9]; - let cctp_message_transmitter_authority = &accounts[10]; - let cctp_message_transmitter_config = &accounts[11]; - let cctp_used_nonces = &accounts[12]; - let cctp_message_transmitter_event_authority = &accounts[13]; - let cctp_token_messenger = &accounts[14]; - let cctp_remote_token_messenger = &accounts[15]; - let cctp_token_minter = &accounts[16]; - let cctp_local_token = &accounts[17]; - let cctp_token_pair = &accounts[18]; - let cctp_token_messenger_minter_custody_token = &accounts[19]; - let cctp_token_messenger_minter_event_authority = &accounts[20]; - let cctp_token_messenger_minter_program = &accounts[21]; - let cctp_message_transmitter_program = &accounts[22]; - let guardian_set = &accounts[23]; - let guardian_set_signatures = &accounts[24]; - let verify_shim_program = &accounts[25]; - let token_program = &accounts[26]; - let system_program = &accounts[27]; - let receive_message_args = data.to_receive_message_args(); - let finalized_vaa_message_args = data.finalized_vaa_message_args; - - let cctp_message = CctpMessage::parse(&receive_message_args.encoded_message) - .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; - - // Load accounts - let fast_market_order_zero_copy = - super::helpers::try_fast_market_order_account(fast_market_order)?; - // Create pdas for addresses that need to be created - // Check the prepared order response account is valid - let fast_market_order_digest = fast_market_order_zero_copy.digest(); - - require_eq!( - cctp_mint_recipient.key(), - CCTP_MINT_RECIPIENT, - MatchingEngineError::InvalidMintRecipient - ); + let payer_info = &accounts[0]; - // Check that fast market order is owned by the program - require!( - fast_market_order.owner == program_id, - ErrorCode::ConstraintOwner - ); + let custodian_info = &accounts[1]; + super::helpers::try_custodian_account(custodian_info, false)?; - // Check custodian owner - super::helpers::require_owned_by_this_program(custodian, "custodian")?; - // Check that custodian deserializes correctly - let _checked_custodian = - Custodian::try_deserialize(&mut &custodian.data.borrow()[..]).map(Box::new)?; - // Deserialize the to_endpoint account + let fast_market_order = super::helpers::try_fast_market_order_account(&accounts[2])?; - let to_endpoint_account = - RouterEndpoint::try_deserialize(&mut &to_endpoint.data.borrow()[..]).map(Box::new)?; - // Deserialize the from_endpoint account - let from_endpoint_account = - RouterEndpoint::try_deserialize(&mut &from_endpoint.data.borrow()[..]).map(Box::new)?; - - let guardian_set_bump = finalized_vaa_message_args.guardian_set_bump; - - let prepared_order_response_seeds = [ - PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_digest, - ]; - - let (prepared_order_response_pda, prepared_order_response_bump) = - Pubkey::find_program_address(&prepared_order_response_seeds, program_id); - - let prepared_custody_token_seeds = [ - crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, - prepared_order_response_pda.as_ref(), - ]; - - let (prepared_custody_token_pda, prepared_custody_token_bump) = - Pubkey::find_program_address(&prepared_custody_token_seeds, program_id); - - // Check usdc mint - require_eq!( - usdc.key(), - common::USDC_MINT, - MatchingEngineError::InvalidMint - ); - - // Check from_endpoint owner - require_eq!(from_endpoint.owner, program_id, ErrorCode::ConstraintOwner); - - // Check to_endpoint owner - require_eq!(to_endpoint.owner, program_id, ErrorCode::ConstraintOwner); - - // Check that the from and to endpoints are different - require_neq!( - from_endpoint_account.chain, - to_endpoint_account.chain, - MatchingEngineError::SameEndpoint - ); + let (from_endpoint, to_endpoint) = + super::helpers::try_live_endpoint_accounts_path(&accounts[3], &accounts[4])?; // Check that the to endpoint protocol is cctp or local require!( matches!( - to_endpoint_account.protocol, + to_endpoint.protocol, MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } ), MatchingEngineError::InvalidEndpoint ); - // Check that to endpoint chain is equal to the fast_market_order target_chain + // The destination registered endpoint must match the fast market order's + // target chain. We cache this endpoint's info in the new prepared order + // response account. require_eq!( - to_endpoint_account.chain, - fast_market_order_zero_copy.target_chain, + to_endpoint.chain, + fast_market_order.target_chain, MatchingEngineError::InvalidTargetRouter ); - // Check that the prepared order response pda is equal to the prepared order response account key - require_eq!( - prepared_order_response_pda, - prepared_order_response.key(), - MatchingEngineError::InvalidPda - ); + // These accounts will be created by the end of this instruction. + let new_prepared_order_response_info = &accounts[5]; + let new_prepared_custody_info = &accounts[6]; - // Check that the prepared custody token pda is equal to the prepared custody token account key - require_eq!( - prepared_custody_token_pda, - prepared_custody_token.key(), - MatchingEngineError::InvalidPda - ); + let base_fee_token_info = &accounts[7]; - // Check the base token fee key is not equal to the prepared custody token key - // TODO: Check that base fee token is actually a token account - require_neq!( - base_fee_token.key(), - prepared_custody_token.key(), - MatchingEngineError::InvalidBaseFeeToken - ); + // Unlikely to happen, but we disallow the base fee token account to be the + // same as the new prepared custody token account. + if base_fee_token_info.key == new_prepared_custody_info.key { + return Err(MatchingEngineError::InvalidBaseFeeToken.into()); + } - require_eq!( - token_program.key(), - spl_token::ID, - MatchingEngineError::InvalidProgram - ); + // This account must be the USDC mint. This instruction does not refer to + // this account explicitly. It just needs to exist so that we can create the + // prepared order response's custody token account. + super::helpers::try_usdc_account(&accounts[8])?; + + let PrepareOrderResponseCctpShimData { + encoded_cctp_message, + cctp_attestation, + finalized_vaa_message_args: + FinalizedVaaMessageArgs { + base_fee, + consistency_level: finalized_consistency_level, + guardian_set_bump, + }, + } = data; - require_eq!( - verify_shim_program.key(), - wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - MatchingEngineError::InvalidProgram - ); + // We can generate the finalized VAA message hash using instruction data, + // the fast market order account and the CCTP message contents. The fast + // message is emitted after the finalized message atomically via the Token + // Router. - require_eq!( - system_program.key(), - solana_program::system_program::ID, - MatchingEngineError::InvalidProgram + let cctp_message = CctpMessage::parse(&encoded_cctp_message) + .map_err(|_| MatchingEngineError::InvalidCctpMessage)?; + + let fast_vaa_timestamp = fast_market_order.vaa_timestamp; + let source_chain = fast_market_order.vaa_emitter_chain; + let amount_in = fast_market_order.amount_in; + + let finalized_message_digest = wormhole_svm_definitions::compute_keccak_digest( + keccak::hashv(&[ + &fast_vaa_timestamp.to_be_bytes(), + &[0, 0, 0, 0], // 0 nonce + &source_chain.to_be_bytes(), + &fast_market_order.vaa_emitter_address, + &fast_market_order + .vaa_sequence + .saturating_sub(1) + .to_be_bytes(), + &[finalized_consistency_level], + &Deposit { + // TODO: I don't believe this is right. This needs to be the + // source token address, which can be found in the CCTP + // token pair account. + token_address: USDC_MINT.to_bytes(), + amount: U256::from(amount_in), + source_cctp_domain: cctp_message.source_domain(), + destination_cctp_domain: cctp_message.destination_domain(), + cctp_nonce: cctp_message.nonce(), + burn_source: from_endpoint.mint_recipient, + mint_recipient: CCTP_MINT_RECIPIENT.to_bytes(), + payload: SlowOrderResponse { base_fee }.to_vec().try_into()?, + } + .to_vec(), + ]), + None, ); - // Construct the finalized vaa message digest data - let finalized_vaa_message_digest = { - let finalized_vaa_timestamp = fast_market_order_zero_copy.vaa_timestamp; - let finalized_vaa_sequence = fast_market_order_zero_copy.vaa_sequence.saturating_sub(1); - let finalized_vaa_emitter_chain = fast_market_order_zero_copy.vaa_emitter_chain; - let finalized_vaa_emitter_address = fast_market_order_zero_copy.vaa_emitter_address; - let finalized_vaa_consistency_level = finalized_vaa_message_args.consistency_level; - let slow_order_response = SlowOrderResponse { - base_fee: finalized_vaa_message_args.base_fee, - }; - let deposit_vaa_payload = Deposit { - token_address: usdc.key().to_bytes(), - amount: U256::from(fast_market_order_zero_copy.amount_in), - source_cctp_domain: cctp_message.source_domain(), - destination_cctp_domain: cctp_message.destination_domain(), - cctp_nonce: cctp_message.nonce(), - burn_source: from_endpoint_account.mint_recipient, - mint_recipient: cctp_mint_recipient.key().to_bytes(), - payload: WriteableBytes::new(slow_order_response.to_vec()), - }; - - finalized_vaa_message_args.digest( - VaaMessageBodyHeader::new( - finalized_vaa_consistency_level, - finalized_vaa_timestamp, - finalized_vaa_sequence, - finalized_vaa_emitter_chain, - finalized_vaa_emitter_address, - ), - deposit_vaa_payload, - ) - }; + let verify_vaa_shim_program_info = &accounts[9]; - // Verify deposit message shim using verify shim program + if verify_vaa_shim_program_info.key + != &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID + { + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_account_name("verify_vaa_shim_program")); + } - // Start verify deposit message vaa shim - // ------------------------------------------------------------------------------------------------ - let verify_hash_data = { - let mut data = vec![]; - data.extend_from_slice( - &wormhole_svm_shim::verify_vaa::VerifyVaaShimInstruction::::VERIFY_HASH_SELECTOR, - ); - data.push(guardian_set_bump); - data.extend_from_slice(&finalized_vaa_message_digest); - data - }; + let wormhole_guardian_set_info = &accounts[10]; + let shim_guardian_signatures_info = &accounts[11]; - let verify_shim_ix = Instruction { - program_id: wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: vec![ - AccountMeta::new_readonly(guardian_set.key(), false), - AccountMeta::new_readonly(guardian_set_signatures.key(), false), - ], - data: verify_hash_data, - }; - invoke_signed_unchecked(&verify_shim_ix, accounts, &[])?; - // End verify deposit message vaa shim - // ------------------------------------------------------------------------------------------------ + let verify_hash_ix = VerifyHash { + program_id: verify_vaa_shim_program_info.key, + accounts: VerifyHashAccounts { + guardian_set: wormhole_guardian_set_info.key, + guardian_signatures: shim_guardian_signatures_info.key, + }, + data: VerifyHashData::new(guardian_set_bump, finalized_message_digest), + } + .instruction(); - // Start create prepared order response account - // ------------------------------------------------------------------------------------------------ + invoke_signed_unchecked(&verify_hash_ix, accounts, &[])?; - // Write to the prepared slow order account, which will be closed by one of the following - // instructions: + // Write to the prepared slow order account, which will be closed by one of + // the following instructions: // * settle_auction_active_cctp // * settle_auction_complete // * settle_auction_none - let create_prepared_order_respone_seeds = [ - PreparedOrderResponse::SEED_PREFIX, - &fast_market_order_digest, - &[prepared_order_response_bump], - ]; - let prepared_order_response_signer_seeds = &[&create_prepared_order_respone_seeds[..]]; - let prepared_order_response_account_space = PreparedOrderResponse::compute_size( - fast_market_order_zero_copy.redeemer_message_length.into(), - ); - create_account_reliably( - &signer.key(), - &prepared_order_response.key(), - prepared_order_response.lamports(), - prepared_order_response_account_space, + let fast_market_order_digest = fast_market_order.digest(); + + let (expected_prepared_order_response_key, prepared_order_response_bump) = + Pubkey::find_program_address( + &[ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_digest, + ], + &ID, + ); + + super::helpers::create_account_reliably( + payer_info.key, + &expected_prepared_order_response_key, + new_prepared_order_response_info.lamports(), + PreparedOrderResponse::compute_size(fast_market_order.redeemer_message_length.into()), accounts, - program_id, - prepared_order_response_signer_seeds, + &ID, + &[&[ + PreparedOrderResponse::SEED_PREFIX, + &fast_market_order_digest, + &[prepared_order_response_bump], + ]], )?; - // Write the prepared order response account data ... - let prepared_order_response_account_to_write = PreparedOrderResponse { - seeds: PreparedOrderResponseSeeds { - fast_vaa_hash: fast_market_order_digest, - bump: prepared_order_response_bump, - }, - info: PreparedOrderResponseInfo { - prepared_by: signer.key(), - base_fee_token: base_fee_token.key(), - source_chain: fast_market_order_zero_copy.vaa_emitter_chain, - base_fee: finalized_vaa_message_args.base_fee, - fast_vaa_timestamp: fast_market_order_zero_copy.vaa_timestamp, - amount_in: fast_market_order_zero_copy.amount_in, - sender: fast_market_order_zero_copy.sender, - redeemer: fast_market_order_zero_copy.redeemer, - init_auction_fee: fast_market_order_zero_copy.init_auction_fee, - }, - to_endpoint: to_endpoint_account.info, - redeemer_message: fast_market_order_zero_copy.redeemer_message - [..usize::from(fast_market_order_zero_copy.redeemer_message_length)] - .to_vec(), - }; - // Use cursor in order to write the prepared order response account data - let prepared_order_response_data: &mut [u8] = &mut prepared_order_response - .try_borrow_mut_data() - .map_err(|_| MatchingEngineError::AccountNotWritable)?; - let mut cursor = Cursor::new(prepared_order_response_data); - prepared_order_response_account_to_write - .try_serialize(&mut cursor) - .map_err(|_| MatchingEngineError::BorshDeserializationError)?; - // End create prepared order response account - // ------------------------------------------------------------------------------------------------ - - // Start create prepared custody token account - // ------------------------------------------------------------------------------------------------ - let create_prepared_custody_token_seeds = [ - crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, - prepared_order_response_pda.as_ref(), - &[prepared_custody_token_bump], - ]; - - let prepared_custody_token_signer_seeds = &[&create_prepared_custody_token_seeds[..]]; + + let (expected_prepared_custody_key, prepared_custody_token_bump) = Pubkey::find_program_address( + &[ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + expected_prepared_order_response_key.as_ref(), + ], + &ID, + ); + create_usdc_token_account_reliably( - &signer.key(), - &prepared_custody_token_pda, - &prepared_order_response_pda, - prepared_custody_token.lamports(), + payer_info.key, + &expected_prepared_custody_key, + &expected_prepared_order_response_key, + new_prepared_custody_info.lamports(), accounts, - prepared_custody_token_signer_seeds, + &[&[ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + expected_prepared_order_response_key.as_ref(), + &[prepared_custody_token_bump], + ]], )?; - // End create prepared custody token account - // ------------------------------------------------------------------------------------------------ + // Mint the USDC to the matching engine's mint recipient, which will be + // transferred to the newly created custody token account immediately after. + + let cctp_message_transmitter_program = &accounts[12]; + + if cctp_message_transmitter_program.key != &CCTP_MESSAGE_TRANSMITTER_PROGRAM_ID { + return Err(ErrorCode::ConstraintAddress.into()).map_err(|e: Error| { + e.with_account_name("token_messenger_minter_program") + .with_pubkeys(( + *cctp_message_transmitter_program.key, + CCTP_MESSAGE_TRANSMITTER_PROGRAM_ID, + )) + }); + }; + + // These accounts will be used later when we invoke the CCTP Message + // Transmitter to mint USDC via the CCTP Token Messenger Minter program. + + let cctp_message_transmitter_authority_info = &accounts[13]; + let cctp_message_transmitter_config_info = &accounts[14]; + let cctp_used_nonces_info = &accounts[15]; + let cctp_message_transmitter_event_authority_info = &accounts[16]; + let cctp_token_messenger_info = &accounts[17]; + let cctp_remote_token_messenger_info = &accounts[18]; + let cctp_token_minter_info = &accounts[19]; + let cctp_local_token_info = &accounts[20]; + let cctp_token_pair_info = &accounts[21]; + let cctp_token_messenger_minter_custody_token_info = &accounts[22]; + let cctp_token_messenger_minter_event_authority_info = &accounts[23]; + let cctp_token_messenger_minter_program_info = &accounts[24]; + let cctp_mint_recipient_info = &accounts[25]; + let token_program_info = &accounts[26]; + let system_program_info = &accounts[27]; - // Create cpi context for verify_vaa_and_mint message_transmitter_program::cpi::receive_token_messenger_minter_message( CpiContext::new_with_signer( cctp_message_transmitter_program.to_account_info(), message_transmitter_program::cpi::ReceiveTokenMessengerMinterMessage { - payer: signer.to_account_info(), - caller: custodian.to_account_info(), - message_transmitter_authority: cctp_message_transmitter_authority.to_account_info(), - message_transmitter_config: cctp_message_transmitter_config.to_account_info(), - used_nonces: cctp_used_nonces.to_account_info(), - token_messenger_minter_program: cctp_token_messenger_minter_program + payer: payer_info.to_account_info(), + caller: custodian_info.to_account_info(), + message_transmitter_authority: cctp_message_transmitter_authority_info .to_account_info(), - system_program: system_program.to_account_info(), - message_transmitter_event_authority: cctp_message_transmitter_event_authority + message_transmitter_config: cctp_message_transmitter_config_info.to_account_info(), + used_nonces: cctp_used_nonces_info.to_account_info(), + token_messenger_minter_program: cctp_token_messenger_minter_program_info .to_account_info(), - message_transmitter_program: cctp_message_transmitter_program.to_account_info(), - token_messenger: cctp_token_messenger.to_account_info(), - remote_token_messenger: cctp_remote_token_messenger.to_account_info(), - token_minter: cctp_token_minter.to_account_info(), - local_token: cctp_local_token.to_account_info(), - token_pair: cctp_token_pair.to_account_info(), - mint_recipient: cctp_mint_recipient.to_account_info(), - custody_token: cctp_token_messenger_minter_custody_token.to_account_info(), - token_program: token_program.to_account_info(), - token_messenger_minter_event_authority: cctp_token_messenger_minter_event_authority + system_program: system_program_info.to_account_info(), + message_transmitter_event_authority: cctp_message_transmitter_event_authority_info .to_account_info(), + message_transmitter_program: cctp_message_transmitter_program.to_account_info(), + token_messenger: cctp_token_messenger_info.to_account_info(), + remote_token_messenger: cctp_remote_token_messenger_info.to_account_info(), + token_minter: cctp_token_minter_info.to_account_info(), + local_token: cctp_local_token_info.to_account_info(), + token_pair: cctp_token_pair_info.to_account_info(), + mint_recipient: cctp_mint_recipient_info.to_account_info(), + custody_token: cctp_token_messenger_minter_custody_token_info.to_account_info(), + token_program: token_program_info.to_account_info(), + token_messenger_minter_event_authority: + cctp_token_messenger_minter_event_authority_info.to_account_info(), }, &[Custodian::SIGNER_SEEDS], ), - receive_message_args, + ReceiveMessageArgs { + encoded_message: encoded_cctp_message, + attestation: cctp_attestation, + }, )?; // Finally transfer minted via CCTP to prepared custody token. let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, - &cctp_mint_recipient.key(), - &prepared_custody_token.key(), - &custodian.key(), + &CCTP_MINT_RECIPIENT, + &expected_prepared_custody_key, + &custodian_info.key, &[], // Apparently this is only for multi-sig accounts - fast_market_order_zero_copy.amount_in, + amount_in, ) .unwrap(); - invoke_signed_unchecked(&transfer_ix, accounts, &[Custodian::SIGNER_SEEDS]) - .map_err(|_| MatchingEngineError::TokenTransferFailed)?; + invoke_signed_unchecked(&transfer_ix, accounts, &[Custodian::SIGNER_SEEDS])?; + + // Finally serialize the prepared order response data into the newly created + // account. + let new_prepared_order_response_info_data: &mut [u8] = + &mut new_prepared_order_response_info.try_borrow_mut_data()?; + let mut new_prepared_order_response_cursor = + std::io::Cursor::new(new_prepared_order_response_info_data); - Ok(()) + PreparedOrderResponse { + seeds: PreparedOrderResponseSeeds { + fast_vaa_hash: fast_market_order_digest, + bump: prepared_order_response_bump, + }, + info: PreparedOrderResponseInfo { + prepared_by: *payer_info.key, + base_fee_token: *base_fee_token_info.key, + source_chain, + base_fee, + fast_vaa_timestamp, + amount_in, + sender: fast_market_order.sender, + redeemer: fast_market_order.redeemer, + init_auction_fee: fast_market_order.init_auction_fee, + }, + to_endpoint: to_endpoint.info, + redeemer_message: fast_market_order.redeemer_message + [..fast_market_order.redeemer_message_length.into()] + .to_vec(), + } + .try_serialize(&mut new_prepared_order_response_cursor) } diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index df56f0366..a08692b24 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -88,13 +88,9 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { Some(Self::ExecuteOrderCctpShim) } FallbackMatchingEngineInstruction::PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR => { - // TODO: Fix this - Some(Self::PrepareOrderResponseCctpShim( - PrepareOrderResponseCctpShimData::from_bytes( - &instruction_data[SELECTOR_SIZE..], - ) - .unwrap(), - )) + borsh::BorshDeserialize::deserialize(&mut &instruction_data[SELECTOR_SIZE..]) + .ok() + .map(Self::PrepareOrderResponseCctpShim) } _ => None, } From a2c43b4e14cee87f02ce6f37d21cb9e18db3c3e8 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Wed, 14 May 2025 16:20:49 -0500 Subject: [PATCH 106/112] solana: more clean up --- .../processor/close_fast_market_order.rs | 32 +++- .../src/fallback/processor/execute_order.rs | 124 ++++++++++---- .../src/fallback/processor/helpers.rs | 29 ++++ .../processor/initialize_fast_market_order.rs | 132 +++++++++------ .../fallback/processor/place_initial_offer.rs | 136 ++++++++++------ .../processor/prepare_order_response.rs | 154 +++++++++++------- 6 files changed, 411 insertions(+), 196 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 4ad029d45..7524d8b85 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -3,6 +3,8 @@ use solana_program::instruction::Instruction; use crate::error::MatchingEngineError; +const NUM_ACCOUNTS: usize = 2; + pub struct CloseFastMarketOrderAccounts<'ix> { /// The fast market order account to be closed. pub fast_market_order: &'ix Pubkey, @@ -26,19 +28,22 @@ impl CloseFastMarketOrder<'_> { close_account_refund_recipient: refund_recipient, } = self.accounts; + let accounts = vec![ + AccountMeta::new(*fast_market_order, false), + AccountMeta::new(*refund_recipient, true), + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new(*fast_market_order, false), - AccountMeta::new(*refund_recipient, true), - ], + accounts, data: super::FallbackMatchingEngineInstruction::CloseFastMarketOrder.to_vec(), } } } pub fn process(accounts: &[AccountInfo]) -> Result<()> { - super::helpers::require_min_account_infos_len(accounts, 2)?; + super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; // We need to check the refund recipient account against what we know as the // refund recipient encoded in the fast market order account. @@ -75,3 +80,20 @@ pub fn process(accounts: &[AccountInfo]) -> Result<()> { Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_instruction() { + CloseFastMarketOrder { + program_id: &Default::default(), + accounts: CloseFastMarketOrderAccounts { + fast_market_order: &Default::default(), + close_account_refund_recipient: &Default::default(), + }, + } + .instruction(); + } +} diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 2a3025b2b..71345d6e5 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -16,11 +16,13 @@ use crate::{ use super::burn_and_post::{burn_and_post, PostMessageAccounts}; +const NUM_ACCOUNTS: usize = 32; + // TODO: Rename to "ExecuteOrderCctpV2Accounts". #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExecuteOrderShimAccounts<'ix> { /// The signer account. - // TODO: Rename payer. + // TODO: Rename to "payer". pub signer: &'ix Pubkey, // 0 /// The cctp message account. Seeds must be \["cctp-msg", auction_address.as_ref()\]. // TODO: Rename to "new_cctp_message". @@ -142,42 +144,45 @@ impl ExecuteOrderCctpShim<'_> { clock: _, } = self.accounts; + let accounts = vec![ + AccountMeta::new(*payer, true), + AccountMeta::new(*new_cctp_message, false), + AccountMeta::new(*custodian, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new(*active_auction, false), + AccountMeta::new(*auction_custody, false), + AccountMeta::new_readonly(*auction_config, false), + AccountMeta::new(*auction_best_offer_token, false), + AccountMeta::new(*executor_token, false), + AccountMeta::new(*auction_initial_offer_token, false), + AccountMeta::new(*auction_initial_participant, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new_readonly(*post_message_shim_program, false), + AccountMeta::new(*core_bridge_emitter_sequence, false), + AccountMeta::new(*shim_message, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), + AccountMeta::new(*cctp_mint, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), + AccountMeta::new(*cctp_message_transmitter_config, false), + AccountMeta::new_readonly(*cctp_token_messenger, false), + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), + AccountMeta::new_readonly(*cctp_token_minter, false), + AccountMeta::new(*cctp_local_token, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*core_bridge_program, false), + AccountMeta::new(*core_bridge_config, false), + AccountMeta::new(*core_bridge_fee_collector, false), + AccountMeta::new(*post_message_shim_event_authority, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(solana_program::sysvar::clock::ID, false), + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new(*payer, true), - AccountMeta::new(*new_cctp_message, false), - AccountMeta::new(*custodian, false), - AccountMeta::new_readonly(*fast_market_order, false), - AccountMeta::new(*active_auction, false), - AccountMeta::new(*auction_custody, false), - AccountMeta::new_readonly(*auction_config, false), - AccountMeta::new(*auction_best_offer_token, false), - AccountMeta::new(*executor_token, false), - AccountMeta::new(*auction_initial_offer_token, false), - AccountMeta::new(*auction_initial_participant, false), - AccountMeta::new_readonly(*to_endpoint, false), - AccountMeta::new_readonly(*post_message_shim_program, false), - AccountMeta::new(*core_bridge_emitter_sequence, false), - AccountMeta::new(*shim_message, false), - AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), - AccountMeta::new(*cctp_mint, false), - AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), - AccountMeta::new(*cctp_message_transmitter_config, false), - AccountMeta::new_readonly(*cctp_token_messenger, false), - AccountMeta::new_readonly(*cctp_remote_token_messenger, false), - AccountMeta::new_readonly(*cctp_token_minter, false), - AccountMeta::new(*cctp_local_token, false), - AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), - AccountMeta::new_readonly(*cctp_message_transmitter_program, false), - AccountMeta::new_readonly(*core_bridge_program, false), - AccountMeta::new(*core_bridge_config, false), - AccountMeta::new(*core_bridge_fee_collector, false), - AccountMeta::new(*post_message_shim_event_authority, false), - AccountMeta::new_readonly(solana_program::system_program::ID, false), - AccountMeta::new_readonly(spl_token::ID, false), - AccountMeta::new_readonly(solana_program::sysvar::clock::ID, false), - ], + accounts, data: super::FallbackMatchingEngineInstruction::ExecuteOrderCctpShim.to_vec(), } } @@ -185,7 +190,7 @@ impl ExecuteOrderCctpShim<'_> { pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not - super::helpers::require_min_account_infos_len(accounts, 31)?; + super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; // Get the accounts let payer_info = &accounts[0]; @@ -638,3 +643,50 @@ pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { invoke_signed_unchecked(&close_account_ix, accounts, &[Custodian::SIGNER_SEEDS]) .map_err(Into::into) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_instruction() { + ExecuteOrderCctpShim { + program_id: &Default::default(), + accounts: ExecuteOrderShimAccounts { + signer: &Default::default(), + cctp_message: &Default::default(), + custodian: &Default::default(), + fast_market_order: &Default::default(), + active_auction: &Default::default(), + active_auction_custody_token: &Default::default(), + active_auction_config: &Default::default(), + active_auction_best_offer_token: &Default::default(), + executor_token: &Default::default(), + initial_offer_token: &Default::default(), + initial_participant: &Default::default(), + to_router_endpoint: &Default::default(), + post_message_shim_program: &Default::default(), + core_bridge_emitter_sequence: &Default::default(), + post_shim_message: &Default::default(), + cctp_deposit_for_burn_mint: &Default::default(), + cctp_deposit_for_burn_token_messenger_minter_sender_authority: &Default::default(), + cctp_deposit_for_burn_message_transmitter_config: &Default::default(), + cctp_deposit_for_burn_token_messenger: &Default::default(), + cctp_deposit_for_burn_remote_token_messenger: &Default::default(), + cctp_deposit_for_burn_token_minter: &Default::default(), + cctp_deposit_for_burn_local_token: &Default::default(), + cctp_deposit_for_burn_token_messenger_minter_event_authority: &Default::default(), + cctp_deposit_for_burn_token_messenger_minter_program: &Default::default(), + cctp_deposit_for_burn_message_transmitter_program: &Default::default(), + core_bridge_program: &Default::default(), + core_bridge_config: &Default::default(), + core_bridge_fee_collector: &Default::default(), + post_message_shim_event_authority: &Default::default(), + system_program: &Default::default(), + token_program: &Default::default(), + clock: &Default::default(), + }, + } + .instruction(); + } +} diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index c7437920e..28b0528e1 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -10,6 +10,7 @@ use solana_program::{ program_pack::Pack, system_instruction, }; +use wormhole_svm_shim::verify_vaa; use crate::{ error::MatchingEngineError, @@ -140,6 +141,34 @@ pub fn try_fast_market_order_account<'a>( })) } +pub fn invoke_verify_hash( + verify_vaa_shim_program_index: usize, + wormhole_guardian_set_index: usize, + shim_guardian_signatures_index: usize, + guardian_set_bump: u8, + vaa_message_digest: keccak::Hash, + accounts: &[AccountInfo], +) -> Result<()> { + if accounts[verify_vaa_shim_program_index].key + != &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID + { + return Err(ErrorCode::ConstraintAddress.into()) + .map_err(|e: Error| e.with_account_name("verify_vaa_shim_program")); + } + + let verify_hash_ix = verify_vaa::VerifyHash { + program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, + accounts: verify_vaa::VerifyHashAccounts { + guardian_set: accounts[wormhole_guardian_set_index].key, + guardian_signatures: accounts[shim_guardian_signatures_index].key, + }, + data: verify_vaa::VerifyHashData::new(guardian_set_bump, vaa_message_digest), + } + .instruction(); + + invoke_signed_unchecked(&verify_hash_ix, accounts, &[]).map_err(Into::into) +} + pub fn create_account_reliably( payer_key: &Pubkey, account_key: &Pubkey, diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index a968959c0..84ee1e6bc 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -1,30 +1,31 @@ use anchor_lang::{prelude::*, Discriminator}; use bytemuck::{Pod, Zeroable}; -use solana_program::{instruction::Instruction, keccak, program::invoke_signed_unchecked}; -use wormhole_svm_shim::verify_vaa; +use solana_program::{instruction::Instruction, keccak}; use crate::{state::FastMarketOrder, ID}; +const NUM_ACCOUNTS: usize = 6; + pub struct InitializeFastMarketOrderAccounts<'ix> { /// Lamports from this signer will be used to create the new fast market /// order account. This account will be the only authority allowed to /// close this account. // TODO: Rename to "payer". - pub signer: &'ix Pubkey, - /// The fast market order account pubkey (that is created by the - /// instruction). - // TODO: Rename to "new_fast_market_order". - pub fast_market_order_account: &'ix Pubkey, + pub signer: &'ix Pubkey, // 0 /// Wormhole guardian set account used to check recovered pubkeys using /// [Self::guardian_set_signatures]. // TODO: Rename to "wormhole_guardian_set" - pub guardian_set: &'ix Pubkey, + pub verify_vaa_shim_program: &'ix Pubkey, // 1 + pub guardian_set: &'ix Pubkey, // 2 /// The guardian set signatures of fast market order VAA. // TODO: Rename to "shim_guardian_signatures". - pub guardian_set_signatures: &'ix Pubkey, - pub verify_vaa_shim_program: &'ix Pubkey, + pub guardian_set_signatures: &'ix Pubkey, // 3 + /// The fast market order account pubkey (that is created by the + /// instruction). + // TODO: Rename to "new_fast_market_order". + pub fast_market_order_account: &'ix Pubkey, // 4 // TODO: Remove. - pub system_program: &'ix Pubkey, + pub system_program: &'ix Pubkey, // 5 } #[derive(Debug, Copy, Clone, Pod, Zeroable)] @@ -40,6 +41,7 @@ pub struct InitializeFastMarketOrderData { impl InitializeFastMarketOrderData { // Adds the padding to the InitializeFastMarketOrderData + // TODO: change FastMarketOrder to FastMarketOrderParams. pub fn new(fast_market_order: FastMarketOrder, guardian_set_bump: u8) -> Self { Self { fast_market_order, @@ -74,16 +76,19 @@ impl InitializeFastMarketOrder<'_> { system_program: _, } = self.accounts; + let accounts = vec![ + AccountMeta::new(*payer, true), + AccountMeta::new_readonly(*verify_vaa_shim_program, false), + AccountMeta::new_readonly(*wormhole_guardian_set, false), + AccountMeta::new_readonly(*shim_guardian_signatures, false), + AccountMeta::new(*new_fast_market_order, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new(*payer, true), - AccountMeta::new(*new_fast_market_order, false), - AccountMeta::new_readonly(*wormhole_guardian_set, false), - AccountMeta::new_readonly(*shim_guardian_signatures, false), - AccountMeta::new_readonly(*verify_vaa_shim_program, false), - AccountMeta::new_readonly(solana_program::system_program::ID, false), - ], + accounts, data: super::FallbackMatchingEngineInstruction::InitializeFastMarketOrder(&self.data) .to_vec(), } @@ -94,7 +99,7 @@ pub(super) fn process( accounts: &[AccountInfo], data: &InitializeFastMarketOrderData, ) -> Result<()> { - super::helpers::require_min_account_infos_len(accounts, 6)?; + super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; let fast_market_order = &data.fast_market_order; @@ -107,8 +112,20 @@ pub(super) fn process( // fast market order account. let payer_info = &accounts[0]; - // Verify that the fast market order account's key is derived correctly. - let new_fast_market_order_info = &accounts[1]; + // Verify the VAA digest with the Verify VAA shim program. + super::helpers::invoke_verify_hash( + 1, // verify_vaa_shim_program_index + 2, // wormhole_guardian_set_index + 3, // shim_guardian_signatures_index + data.guardian_set_bump, + keccak::Hash(fast_market_order_vaa_digest), + accounts, + )?; + + // Create the new fast market order account and serialize the instruction + // data into it. + + let new_fast_market_order_info = &accounts[4]; let (expected_fast_market_order_key, fast_market_order_bump) = Pubkey::find_program_address( &[ FastMarketOrder::SEED_PREFIX, @@ -118,31 +135,6 @@ pub(super) fn process( &ID, ); - // These accounts will be used by the Verify VAA shim program. - let wormhole_guardian_set_info = &accounts[2]; - let shim_guardian_signatures_info = &accounts[3]; - - // Verify the VAA digest with the Verify VAA shim program. - invoke_signed_unchecked( - &verify_vaa::VerifyHash { - program_id: &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID, - accounts: verify_vaa::VerifyHashAccounts { - guardian_set: wormhole_guardian_set_info.key, - guardian_signatures: shim_guardian_signatures_info.key, - }, - data: verify_vaa::VerifyHashData::new( - data.guardian_set_bump, - keccak::Hash(fast_market_order_vaa_digest), - ), - } - .instruction(), - accounts, - &[], - )?; - - // Create the new fast market order account and serialize the instruction - // data into it. - const DISCRIMINATOR_LEN: usize = FastMarketOrder::DISCRIMINATOR.len(); const FAST_MARKET_ORDER_DATA_LEN: usize = DISCRIMINATOR_LEN + std::mem::size_of::(); @@ -174,3 +166,49 @@ pub(super) fn process( Ok(()) } + +#[cfg(test)] +mod test { + use crate::state::FastMarketOrderParams; + + use super::*; + + #[test] + fn test_instruction() { + InitializeFastMarketOrder { + program_id: &Default::default(), + accounts: InitializeFastMarketOrderAccounts { + signer: &Default::default(), + fast_market_order_account: &Default::default(), + verify_vaa_shim_program: &Default::default(), + guardian_set: &Default::default(), + guardian_set_signatures: &Default::default(), + system_program: &Default::default(), + }, + data: InitializeFastMarketOrderData::new( + FastMarketOrder::new(FastMarketOrderParams { + amount_in: Default::default(), + min_amount_out: Default::default(), + deadline: Default::default(), + target_chain: Default::default(), + redeemer_message_length: Default::default(), + redeemer: Default::default(), + sender: Default::default(), + refund_address: Default::default(), + max_fee: Default::default(), + init_auction_fee: Default::default(), + redeemer_message: [0; 512], + close_account_refund_recipient: Default::default(), + vaa_sequence: Default::default(), + vaa_timestamp: Default::default(), + vaa_nonce: Default::default(), + vaa_emitter_chain: Default::default(), + vaa_consistency_level: Default::default(), + vaa_emitter_address: Default::default(), + }), + Default::default(), + ), + } + .instruction(); + } +} diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index 29b7b1678..e92d1522e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -15,6 +15,8 @@ use super::FallbackMatchingEngineInstruction; // TODO: Remove this. pub use super::helpers::VaaMessageBodyHeader; +const NUM_ACCOUNTS: usize = 13; + // TODO: Remove this struct. Just use u64. #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] @@ -27,36 +29,36 @@ pub struct PlaceInitialOfferCctpShimData { pub struct PlaceInitialOfferCctpShimAccounts<'ix> { /// The signer account // TODO: Rename to "payer". - pub signer: &'ix Pubkey, - /// The transfer authority account - pub transfer_authority: &'ix Pubkey, + pub signer: &'ix Pubkey, // 0 /// The custodian account - pub custodian: &'ix Pubkey, + pub custodian: &'ix Pubkey, // 1 /// The auction config account - pub auction_config: &'ix Pubkey, + pub auction_config: &'ix Pubkey, // 2 /// The from endpoint account - pub from_endpoint: &'ix Pubkey, + pub from_endpoint: &'ix Pubkey, // 3 /// The to endpoint account - pub to_endpoint: &'ix Pubkey, + pub to_endpoint: &'ix Pubkey, // 4 /// The fast market order account, which will be initialized. Seeds are /// [FastMarketOrderState::SEED_PREFIX, auction_address.as_ref()] - pub fast_market_order: &'ix Pubkey, + pub fast_market_order: &'ix Pubkey, // 5 /// The auction account, which will be initialized. // TODO: Rename to "new_auction". - pub auction: &'ix Pubkey, + pub auction: &'ix Pubkey, // 6 /// The offer token account - pub offer_token: &'ix Pubkey, + pub offer_token: &'ix Pubkey, // 7 /// The auction custody token account. // TODO: Rename to "new_auction_custody". - pub auction_custody_token: &'ix Pubkey, + pub auction_custody_token: &'ix Pubkey, // 8 /// The usdc token account - pub usdc: &'ix Pubkey, + pub usdc: &'ix Pubkey, // 9 + /// The transfer authority account + pub transfer_authority: &'ix Pubkey, // 10 /// The system program account // TODO: Remove. - pub system_program: &'ix Pubkey, + pub system_program: &'ix Pubkey, // 11 /// The token program account // TODO: Remove. - pub token_program: &'ix Pubkey, + pub token_program: &'ix Pubkey, // 12 } // TODO: Rename to "PlaceInitialOfferCctpV2". @@ -85,23 +87,26 @@ impl PlaceInitialOfferCctpShim<'_> { token_program: _, } = self.accounts; + let accounts = vec![ + AccountMeta::new(*payer, true), + AccountMeta::new_readonly(*custodian, false), + AccountMeta::new_readonly(*auction_config, false), + AccountMeta::new_readonly(*from_endpoint, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new(*new_auction, false), + AccountMeta::new(*offer_token, false), + AccountMeta::new(*new_auction_custody, false), + AccountMeta::new_readonly(*usdc, false), + AccountMeta::new_readonly(*transfer_authority, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new(*payer, true), - AccountMeta::new_readonly(*transfer_authority, false), - AccountMeta::new_readonly(*custodian, false), - AccountMeta::new_readonly(*auction_config, false), - AccountMeta::new_readonly(*from_endpoint, false), - AccountMeta::new_readonly(*to_endpoint, false), - AccountMeta::new_readonly(*fast_market_order, false), - AccountMeta::new(*new_auction, false), - AccountMeta::new(*offer_token, false), - AccountMeta::new(*new_auction_custody, false), - AccountMeta::new_readonly(*usdc, false), - AccountMeta::new_readonly(solana_program::system_program::ID, false), - AccountMeta::new_readonly(spl_token::ID, false), - ], + accounts, data: FallbackMatchingEngineInstruction::PlaceInitialOfferCctpShim(&self.data).to_vec(), } } @@ -109,34 +114,27 @@ impl PlaceInitialOfferCctpShim<'_> { pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { // Check all accounts are valid - super::helpers::require_min_account_infos_len(accounts, 11)?; + super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; // This instruction will use the payer to create the following accounts: // 1. Auction. // 2. Auction Custody Token Account. let payer_info = &accounts[0]; - // This transfer authority must have been delegated authority to transfer - // USDC so it can transfer tokens to the auction custody token account. - // - // We will validate this transfer authority when we attempt to transfer USDC - // to the auction's custody account. - let _transfer_authority = &accounts[1]; - let custodian = super::helpers::try_custodian_account( - &accounts[2], + &accounts[1], true, // check_if_paused )?; let auction_config = super::helpers::try_auction_config_account( - &accounts[3], + &accounts[2], Some(custodian.auction_config_id), )?; let (from_endpoint_account, to_endpoint_account) = - super::helpers::try_live_endpoint_accounts_path(&accounts[4], &accounts[5])?; + super::helpers::try_live_endpoint_accounts_path(&accounts[3], &accounts[4])?; - let fast_market_order = super::helpers::try_fast_market_order_account(&accounts[6])?; + let fast_market_order = super::helpers::try_fast_market_order_account(&accounts[5])?; // Verify the fast market order comes from a registered endpoint. // TODO: Consider moving source endpoint check when creating fast market @@ -158,7 +156,7 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - MatchingEngineError::InvalidTargetRouter ); - let new_auction_info = &accounts[7]; + let new_auction_info = &accounts[6]; let vaa_sequence = fast_market_order.vaa_sequence; let vaa_timestamp = fast_market_order.vaa_timestamp; @@ -180,11 +178,6 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - let (expected_auction_key, new_auction_bump) = Pubkey::find_program_address(&[Auction::SEED_PREFIX, &vaa_message_digest.0], &ID); - // This account must be the USDC mint. This instruction does not refer to - // this account explicitly. It just needs to exist so that we can create the - // auction's custody token account. - super::helpers::try_usdc_account(&accounts[10])?; - // Check that the to endpoint is a valid protocol match to_endpoint_account.protocol { MessageProtocol::Cctp { .. } | MessageProtocol::Local { .. } => (), @@ -213,8 +206,8 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - // We will need to move USDC from the offer token account to the custody // token account. The custody token account will need to be created first. - let offer_token_info = &accounts[8]; - let new_auction_custody_info = &accounts[9]; + let offer_token_info = &accounts[7]; + let new_auction_custody_info = &accounts[8]; // We will use the expected auction custody token account key to create this // account. @@ -226,10 +219,15 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - &ID, ); + // This account must be the USDC mint. This instruction does not refer to + // this account explicitly. It just needs to exist so that we can create the + // auction's custody token account. + super::helpers::try_usdc_account(&accounts[9])?; + super::helpers::create_usdc_token_account_reliably( payer_info.key, &expected_auction_custody_key, - new_auction_info.key, + &expected_auction_key, new_auction_custody_info.lamports(), accounts, &[&[ @@ -239,6 +237,13 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - ]], )?; + // This transfer authority must have been delegated authority to transfer + // USDC so it can transfer tokens to the auction custody token account. + // + // We will validate this transfer authority when we attempt to transfer USDC + // to the auction's custody account. + let _transfer_authority = &accounts[10]; + // We will use the expected transfer authority account key to invoke the // SPL token transfer instruction. let (expected_transfer_authority_key, transfer_authority_bump) = Pubkey::find_program_address( @@ -262,7 +267,7 @@ pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) - let transfer_ix = spl_token::instruction::transfer( &spl_token::ID, offer_token_info.key, - new_auction_custody_info.key, + &expected_auction_custody_key, &expected_transfer_authority_key, &[], fast_market_order @@ -356,6 +361,33 @@ mod tests { vaa_emitter_address: [0_u8; 32], }); let bytes = bytemuck::bytes_of(&test_fast_market_order); - assert!(bytes.len() == std::mem::size_of::()); + // TODO: Maybe change this test to check serialization instead? + assert_eq!(bytes.len(), std::mem::size_of::()); + } + + #[test] + fn test_instruction() { + PlaceInitialOfferCctpShim { + program_id: &Default::default(), + accounts: PlaceInitialOfferCctpShimAccounts { + signer: &Default::default(), + custodian: &Default::default(), + auction_config: &Default::default(), + from_endpoint: &Default::default(), + to_endpoint: &Default::default(), + fast_market_order: &Default::default(), + auction: &Default::default(), + auction_custody_token: &Default::default(), + offer_token: &Default::default(), + usdc: &Default::default(), + transfer_authority: &Default::default(), + system_program: &Default::default(), + token_program: &Default::default(), + }, + data: PlaceInitialOfferCctpShimData { + offer_price: Default::default(), + }, + } + .instruction(); } } diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index aba9487f7..27f7e39e9 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -14,7 +14,6 @@ use common::{ }; use ruint::aliases::U256; use solana_program::{instruction::Instruction, keccak, program::invoke_signed_unchecked}; -use wormhole_svm_shim::verify_vaa::{VerifyHash, VerifyHashAccounts, VerifyHashData}; use crate::{ error::MatchingEngineError, @@ -26,6 +25,8 @@ use crate::{ CCTP_MINT_RECIPIENT, ID, }; +const NUM_ACCOUNTS: usize = 28; + #[derive(BorshDeserialize, BorshSerialize)] pub struct PrepareOrderResponseCctpShimData { pub encoded_cctp_message: Vec, @@ -35,7 +36,7 @@ pub struct PrepareOrderResponseCctpShimData { #[derive(BorshDeserialize, BorshSerialize)] pub struct FinalizedVaaMessageArgs { - pub base_fee: u64, // Can also get from deposit payload + pub base_fee: u64, pub consistency_level: u8, pub guardian_set_bump: u8, } @@ -112,38 +113,41 @@ impl<'ix> PrepareOrderResponseCctpShim<'ix> { system_program: _, } = self.accounts; + let accounts = vec![ + AccountMeta::new(*signer, true), + AccountMeta::new_readonly(*custodian, false), + AccountMeta::new_readonly(*fast_market_order, false), + AccountMeta::new_readonly(*from_endpoint, false), + AccountMeta::new_readonly(*to_endpoint, false), + AccountMeta::new(*prepared_order_response, false), + AccountMeta::new(*prepared_custody_token, false), + AccountMeta::new_readonly(*base_fee_token, false), + AccountMeta::new_readonly(*usdc, false), + AccountMeta::new_readonly(*verify_shim_program, false), + AccountMeta::new_readonly(*guardian_set, false), + AccountMeta::new_readonly(*guardian_set_signatures, false), + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), + AccountMeta::new_readonly(*cctp_message_transmitter_authority, false), + AccountMeta::new_readonly(*cctp_message_transmitter_config, false), + AccountMeta::new(*cctp_used_nonces, false), + AccountMeta::new_readonly(*cctp_message_transmitter_event_authority, false), + AccountMeta::new_readonly(*cctp_token_messenger, false), + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), + AccountMeta::new_readonly(*cctp_token_minter, false), + AccountMeta::new(*cctp_local_token, false), + AccountMeta::new_readonly(*cctp_token_pair, false), + AccountMeta::new(*cctp_token_messenger_minter_custody_token, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), + AccountMeta::new(*cctp_mint_recipient, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(solana_program::system_program::ID, false), + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new(*signer, true), - AccountMeta::new_readonly(*custodian, false), - AccountMeta::new_readonly(*fast_market_order, false), - AccountMeta::new_readonly(*from_endpoint, false), - AccountMeta::new_readonly(*to_endpoint, false), - AccountMeta::new(*prepared_order_response, false), - AccountMeta::new(*prepared_custody_token, false), - AccountMeta::new_readonly(*base_fee_token, false), - AccountMeta::new_readonly(*usdc, false), - AccountMeta::new_readonly(*verify_shim_program, false), - AccountMeta::new_readonly(*guardian_set, false), - AccountMeta::new_readonly(*guardian_set_signatures, false), - AccountMeta::new_readonly(*cctp_message_transmitter_program, false), - AccountMeta::new_readonly(*cctp_message_transmitter_authority, false), - AccountMeta::new_readonly(*cctp_message_transmitter_config, false), - AccountMeta::new(*cctp_used_nonces, false), - AccountMeta::new_readonly(*cctp_message_transmitter_event_authority, false), - AccountMeta::new_readonly(*cctp_token_messenger, false), - AccountMeta::new_readonly(*cctp_remote_token_messenger, false), - AccountMeta::new_readonly(*cctp_token_minter, false), - AccountMeta::new(*cctp_local_token, false), - AccountMeta::new_readonly(*cctp_token_pair, false), - AccountMeta::new(*cctp_token_messenger_minter_custody_token, false), - AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), - AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), - AccountMeta::new(*cctp_mint_recipient, false), - AccountMeta::new_readonly(spl_token::ID, false), - AccountMeta::new_readonly(solana_program::system_program::ID, false), - ], + accounts, data: super::FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(self.data) .to_vec(), } @@ -154,7 +158,7 @@ pub fn prepare_order_response_cctp_shim( accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData, ) -> Result<()> { - require_min_account_infos_len(accounts, 27)?; + require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; let payer_info = &accounts[0]; @@ -253,29 +257,15 @@ pub fn prepare_order_response_cctp_shim( None, ); - let verify_vaa_shim_program_info = &accounts[9]; - - if verify_vaa_shim_program_info.key - != &wormhole_svm_definitions::solana::VERIFY_VAA_SHIM_PROGRAM_ID - { - return Err(ErrorCode::ConstraintAddress.into()) - .map_err(|e: Error| e.with_account_name("verify_vaa_shim_program")); - } - - let wormhole_guardian_set_info = &accounts[10]; - let shim_guardian_signatures_info = &accounts[11]; - - let verify_hash_ix = VerifyHash { - program_id: verify_vaa_shim_program_info.key, - accounts: VerifyHashAccounts { - guardian_set: wormhole_guardian_set_info.key, - guardian_signatures: shim_guardian_signatures_info.key, - }, - data: VerifyHashData::new(guardian_set_bump, finalized_message_digest), - } - .instruction(); - - invoke_signed_unchecked(&verify_hash_ix, accounts, &[])?; + // Verify the VAA digest with the Verify VAA shim program. + super::helpers::invoke_verify_hash( + 9, // verify_vaa_shim_program_index + 10, // wormhole_guardian_set_index + 11, // shim_guardian_signatures_index + guardian_set_bump, + finalized_message_digest, + accounts, + )?; // Write to the prepared slow order account, which will be closed by one of // the following instructions: @@ -441,3 +431,55 @@ pub fn prepare_order_response_cctp_shim( } .try_serialize(&mut new_prepared_order_response_cursor) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_instruction() { + PrepareOrderResponseCctpShim { + program_id: &Default::default(), + accounts: PrepareOrderResponseCctpShimAccounts { + signer: &Default::default(), + custodian: &Default::default(), + fast_market_order: &Default::default(), + from_endpoint: &Default::default(), + to_endpoint: &Default::default(), + prepared_order_response: &Default::default(), + prepared_custody_token: &Default::default(), + base_fee_token: &Default::default(), + usdc: &Default::default(), + verify_shim_program: &Default::default(), + guardian_set: &Default::default(), + guardian_set_signatures: &Default::default(), + cctp_message_transmitter_program: &Default::default(), + cctp_mint_recipient: &Default::default(), + cctp_message_transmitter_authority: &Default::default(), + cctp_message_transmitter_config: &Default::default(), + cctp_used_nonces: &Default::default(), + cctp_message_transmitter_event_authority: &Default::default(), + cctp_token_messenger: &Default::default(), + cctp_remote_token_messenger: &Default::default(), + cctp_token_minter: &Default::default(), + cctp_local_token: &Default::default(), + cctp_token_pair: &Default::default(), + cctp_token_messenger_minter_custody_token: &Default::default(), + cctp_token_messenger_minter_event_authority: &Default::default(), + cctp_token_messenger_minter_program: &Default::default(), + token_program: &Default::default(), + system_program: &Default::default(), + }, + data: PrepareOrderResponseCctpShimData { + encoded_cctp_message: Default::default(), + cctp_attestation: Default::default(), + finalized_vaa_message_args: FinalizedVaaMessageArgs { + base_fee: Default::default(), + consistency_level: Default::default(), + guardian_set_bump: Default::default(), + }, + }, + } + .instruction(); + } +} From 823ec8832d58e02aff189615c9e4795b3cc6d48c Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Tue, 13 May 2025 19:31:11 +0100 Subject: [PATCH 107/112] settle auction none ix added and tests pass --- .../tests/shimful/mod.rs | 1 + .../tests/shimful/shims_execute_order.rs | 1 + .../shimful/shims_settle_auction_none_cctp.rs | 259 +++++++++ .../tests/shimless/mod.rs | 1 + .../tests/shimless/prepare_order_response.rs | 1 + .../tests/shimless/settle_auction.rs | 1 + .../shimless/settle_auction_none_cctp.rs | 174 +++++++ .../tests/test_scenarios/settle_auction.rs | 227 ++++++++ .../tests/testing_engine/config.rs | 15 + .../tests/testing_engine/engine.rs | 72 +++ .../tests/testing_engine/state.rs | 1 + .../src/fallback/processor/mod.rs | 2 +- .../fallback/processor/process_instruction.rs | 24 + .../processor/settle_auction_none_cctp.rs | 493 ++++++++++++++++++ .../src/processor/auction/settle/none/mod.rs | 8 +- 15 files changed, 1275 insertions(+), 5 deletions(-) create mode 100644 solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs create mode 100644 solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs create mode 100644 solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs diff --git a/solana/modules/matching-engine-testing/tests/shimful/mod.rs b/solana/modules/matching-engine-testing/tests/shimful/mod.rs index dcbf6c9a0..2985cf942 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/mod.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/mod.rs @@ -4,4 +4,5 @@ pub mod post_message; pub mod shims_execute_order; pub mod shims_make_offer; pub mod shims_prepare_order_response; +pub mod shims_settle_auction_none_cctp; pub mod verify_shim; diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index 02c1159b7..fd49419a0 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -7,6 +7,7 @@ use anchor_spl::token::spl_token; use common::wormhole_cctp_solana::cctp::{ MESSAGE_TRANSMITTER_PROGRAM_ID, TOKEN_MESSENGER_MINTER_PROGRAM_ID, }; +use matching_engine::accounts::CctpDepositForBurn; use matching_engine::fallback::execute_order::{ExecuteOrderCctpShim, ExecuteOrderShimAccounts}; use solana_program_test::ProgramTestContext; use solana_sdk::{pubkey::Pubkey, signer::Signer, sysvar::SysvarId}; diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs new file mode 100644 index 000000000..289e3b528 --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs @@ -0,0 +1,259 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::spl_token; +use matching_engine::{ + fallback::{ + settle_auction_none_cctp::{ + SettleAuctionNoneCctpShimAccounts, SettleAuctionNoneCctpShimData, + }, + FallbackMatchingEngineInstruction, + }, + state::Auction, +}; +use solana_program::instruction::Instruction; +use solana_program_test::ProgramTestContext; +use solana_sdk::{signature::Signer, sysvar::SysvarId, transaction::Transaction}; +use wormhole_svm_definitions::solana::{ + CORE_BRIDGE_PROGRAM_ID, POST_MESSAGE_SHIM_EVENT_AUTHORITY, POST_MESSAGE_SHIM_PROGRAM_ID, +}; + +use crate::{ + testing_engine::{ + config::{InstructionConfig, SettleAuctionNoneInstructionConfig}, + setup::TestingContext, + state::{OrderPreparedState, TestingEngineState}, + }, + utils::{ + auction::AuctionState, token_account::SplTokenEnum, CORE_BRIDGE_CONFIG, + CORE_BRIDGE_FEE_COLLECTOR, + }, +}; + +use super::shims_execute_order::{create_cctp_accounts, CctpAccounts}; + +pub struct SettleAuctionNoneCctpShim<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: SettleAuctionNoneCctpShimAccounts<'ix>, + pub data: SettleAuctionNoneCctpShimData, +} + +impl SettleAuctionNoneCctpShim<'_> { + pub fn instruction(self) -> Instruction { + Instruction { + program_id: *self.program_id, + accounts: self.accounts.to_account_metas(), + data: FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(&self.data).to_vec(), + } + } +} + +pub async fn settle_auction_none_shimful( + testing_context: &TestingContext, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &SettleAuctionNoneInstructionConfig, +) -> AuctionState { + let payer_signer = &config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + + let settle_auction_none_cctp_accounts = + create_settle_auction_none_cctp_shimful_accounts(testing_context, current_state, config); + let settle_auction_none_cctp_data = settle_auction_none_cctp_accounts.bumps; + + let settle_auction_none_cctp_ix = SettleAuctionNoneCctpShim { + program_id: &testing_context.get_matching_engine_program_id(), + accounts: settle_auction_none_cctp_accounts.as_ref(), + data: settle_auction_none_cctp_data, + } + .instruction(); + let last_blockhash = test_context.get_new_latest_blockhash().await.unwrap(); + let tx = Transaction::new_signed_with_payer( + &[settle_auction_none_cctp_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + last_blockhash, + ); + testing_context + .execute_and_verify_transaction(test_context, tx, config.expected_error()) + .await; + if config.expected_error().is_some() { + return current_state.auction_state().clone(); + } + + AuctionState::Settled +} + +struct SettleAuctionNoneCctpShimAccountsOwned { + pub payer: Pubkey, + pub post_message_message: Pubkey, + pub post_message_sequence: Pubkey, + pub post_message_shim_event_authority: Pubkey, + pub post_message_shim_program: Pubkey, + pub cctp_message: Pubkey, + pub custodian: Pubkey, + pub fee_recipient_token: Pubkey, + pub closed_prepared_order_response_actor: Pubkey, + pub closed_prepared_order_response: Pubkey, + pub closed_prepared_order_response_custody_token: Pubkey, + pub auction: Pubkey, + pub cctp_mint: Pubkey, + pub cctp_local_token: Pubkey, + pub cctp_token_messenger_minter_event_authority: Pubkey, + pub cctp_remote_token_messenger: Pubkey, + pub cctp_token_messenger: Pubkey, + pub cctp_token_messenger_minter_sender_authority: Pubkey, + pub cctp_token_minter: Pubkey, + pub cctp_token_messenger_minter_program: Pubkey, + pub cctp_message_transmitter_config: Pubkey, + pub cctp_message_transmitter_program: Pubkey, + pub core_bridge_program: Pubkey, + pub core_bridge_fee_collector: Pubkey, + pub core_bridge_config: Pubkey, + pub token_program: Pubkey, + pub system_program: Pubkey, + pub clock: Pubkey, + pub rent: Pubkey, + pub bumps: SettleAuctionNoneCctpShimData, +} + +impl SettleAuctionNoneCctpShimAccountsOwned { + pub fn as_ref(&self) -> SettleAuctionNoneCctpShimAccounts { + SettleAuctionNoneCctpShimAccounts { + payer: &self.payer, + post_shim_message: &self.post_message_message, + core_bridge_emitter_sequence: &self.post_message_sequence, + post_message_shim_event_authority: &self.post_message_shim_event_authority, + post_message_shim_program: &self.post_message_shim_program, + cctp_message: &self.cctp_message, + custodian: &self.custodian, + fee_recipient_token: &self.fee_recipient_token, + closed_prepared_order_response_actor: &self.closed_prepared_order_response_actor, + closed_prepared_order_response: &self.closed_prepared_order_response, + closed_prepared_order_response_custody_token: &self + .closed_prepared_order_response_custody_token, + auction: &self.auction, + cctp_mint: &self.cctp_mint, + cctp_local_token: &self.cctp_local_token, + cctp_token_messenger_minter_event_authority: &self + .cctp_token_messenger_minter_event_authority, + cctp_remote_token_messenger: &self.cctp_remote_token_messenger, + cctp_token_messenger: &self.cctp_token_messenger, + cctp_token_messenger_minter_sender_authority: &self + .cctp_token_messenger_minter_sender_authority, + cctp_token_minter: &self.cctp_token_minter, + cctp_token_messenger_minter_program: &self.cctp_token_messenger_minter_program, + cctp_message_transmitter_config: &self.cctp_message_transmitter_config, + cctp_message_transmitter_program: &self.cctp_message_transmitter_program, + core_bridge_program: &self.core_bridge_program, + core_bridge_fee_collector: &self.core_bridge_fee_collector, + core_bridge_config: &self.core_bridge_config, + token_program: &self.token_program, + system_program: &self.system_program, + clock: &self.clock, + rent: &self.rent, + } + } +} + +fn create_settle_auction_none_cctp_shimful_accounts( + testing_context: &TestingContext, + current_state: &TestingEngineState, + config: &SettleAuctionNoneInstructionConfig, +) -> SettleAuctionNoneCctpShimAccountsOwned { + let payer_signer = &config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + + let order_prepared_state = current_state.order_prepared().unwrap(); + let OrderPreparedState { + prepared_order_response_address, + prepared_custody_token, + base_fee_token, + actor_enum: _, + prepared_by, + } = *order_prepared_state; + + let custodian = current_state + .custodian_address() + .expect("Custodian address not found"); + println!("Settle auction custodian address: {:?}", custodian); + + let fast_market_order = current_state.fast_market_order().unwrap().fast_market_order; + let fast_vaa_hash = fast_market_order.digest(); + let (auction, auction_bump) = Pubkey::find_program_address( + &[Auction::SEED_PREFIX, fast_vaa_hash.as_ref()], + &testing_context.get_matching_engine_program_id(), + ); + + let (cctp_message, cctp_message_bump) = Pubkey::find_program_address( + &[common::CCTP_MESSAGE_SEED_PREFIX, &auction.to_bytes()], + &testing_context.get_matching_engine_program_id(), + ); + + let post_message_sequence = wormhole_svm_definitions::find_emitter_sequence_address( + &custodian, + &CORE_BRIDGE_PROGRAM_ID, + ) + .0; + let post_message_message = wormhole_svm_definitions::find_shim_message_address( + &custodian, + &POST_MESSAGE_SHIM_PROGRAM_ID, + ) + .0; + + let fee_recipient_token = testing_context + .testing_actors + .fee_recipient + .token_account_address(&SplTokenEnum::Usdc) + .unwrap(); + + let CctpAccounts { + mint, + local_token, + token_messenger_minter_event_authority, + remote_token_messenger, + token_messenger, + token_messenger_minter_sender_authority, + token_minter, + token_messenger_minter_program, + message_transmitter_config, + message_transmitter_program, + } = create_cctp_accounts(current_state, testing_context); + SettleAuctionNoneCctpShimAccountsOwned { + payer: payer_signer.pubkey(), + post_message_message, + post_message_sequence, + post_message_shim_event_authority: POST_MESSAGE_SHIM_EVENT_AUTHORITY, + post_message_shim_program: POST_MESSAGE_SHIM_PROGRAM_ID, + cctp_message, + custodian, + fee_recipient_token, + closed_prepared_order_response_actor: prepared_by, + closed_prepared_order_response: prepared_order_response_address, + closed_prepared_order_response_custody_token: prepared_custody_token, + auction, + cctp_mint: mint, + cctp_local_token: local_token, + cctp_token_messenger_minter_event_authority: token_messenger_minter_event_authority, + cctp_remote_token_messenger: remote_token_messenger, + cctp_token_messenger: token_messenger, + cctp_token_messenger_minter_sender_authority: token_messenger_minter_sender_authority, + cctp_token_minter: token_minter, + cctp_token_messenger_minter_program: token_messenger_minter_program, + cctp_message_transmitter_config: message_transmitter_config, + cctp_message_transmitter_program: message_transmitter_program, + core_bridge_program: CORE_BRIDGE_PROGRAM_ID, + core_bridge_fee_collector: CORE_BRIDGE_FEE_COLLECTOR, + core_bridge_config: CORE_BRIDGE_CONFIG, + token_program: spl_token::ID, + system_program: solana_program::system_program::ID, + clock: solana_program::clock::Clock::id(), + rent: solana_program::rent::Rent::id(), + bumps: SettleAuctionNoneCctpShimData { + cctp_message_bump, + auction_bump, + }, + } +} diff --git a/solana/modules/matching-engine-testing/tests/shimless/mod.rs b/solana/modules/matching-engine-testing/tests/shimless/mod.rs index 62fc2076a..862f95894 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/mod.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/mod.rs @@ -6,3 +6,4 @@ pub mod make_offer; pub mod pause_custodian; pub mod prepare_order_response; pub mod settle_auction; +pub mod settle_auction_none_cctp; diff --git a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs index 58d9a5f69..9478ab72a 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/prepare_order_response.rs @@ -292,6 +292,7 @@ pub async fn prepare_order_response_shimless_instruction( prepared_custody_token: prepared_custody_token_pda, base_fee_token: base_fee_token_address, actor_enum: config.actor_enum, + prepared_by: payer_signer.pubkey(), }; (ix, order_prepared_state) } diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index ae26387e6..162274621 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -62,6 +62,7 @@ pub async fn settle_auction_complete( prepared_custody_token, base_fee_token, actor_enum: _, + prepared_by: _, } = *order_prepared_state; let matching_engine_program_id = testing_context.get_matching_engine_program_id(); diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs new file mode 100644 index 000000000..6a45dd56c --- /dev/null +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs @@ -0,0 +1,174 @@ +use crate::testing_engine::config::{InstructionConfig, SettleAuctionNoneInstructionConfig}; +use crate::testing_engine::setup::TestingContext; +use crate::testing_engine::state::{OrderPreparedState, TestingEngineState}; +use crate::utils::auction::AuctionState; +use crate::utils::token_account::SplTokenEnum; +use anchor_lang::prelude::*; +use anchor_lang::{InstructionData, ToAccountMetas}; +use anchor_spl::token::spl_token; +use matching_engine::accounts::RequiredSysvars; +use matching_engine::accounts::{ + CheckedCustodian, ClosePreparedOrderResponse, + SettleAuctionNoneCctp as SettleAuctionNoneCctpAccounts, WormholePublishMessage, +}; +use matching_engine::instruction::SettleAuctionNoneCctp as SettleAuctionNoneCctpIx; +use matching_engine::state::{Auction, PreparedOrderResponse}; +use solana_program_test::ProgramTestContext; +use solana_sdk::instruction::Instruction; +use solana_sdk::signature::Signer; +use solana_sdk::sysvar::SysvarId; +use solana_sdk::transaction::Transaction; +use wormhole_svm_definitions::EVENT_AUTHORITY_SEED; + +use crate::shimful::shims_execute_order::create_cctp_deposit_for_burn; + +/// Settle an auction none shimless +pub async fn settle_auction_none_shimless( + testing_context: &TestingContext, + current_state: &TestingEngineState, + test_context: &mut ProgramTestContext, + config: &SettleAuctionNoneInstructionConfig, +) -> AuctionState { + let payer_signer = &config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + + let settle_auction_none_cctp_accounts = create_settle_auction_none_cctp_shimless_accounts( + test_context, + testing_context, + current_state, + config, + ) + .await; + let settle_auction_none_ix = Instruction { + program_id: testing_context.get_matching_engine_program_id(), + accounts: settle_auction_none_cctp_accounts.to_account_metas(None), + data: SettleAuctionNoneCctpIx {}.data(), + }; + let tx = Transaction::new_signed_with_payer( + &[settle_auction_none_ix], + Some(&payer_signer.pubkey()), + &[&payer_signer], + testing_context + .get_new_latest_blockhash(test_context) + .await + .unwrap(), + ); + + testing_context + .execute_and_verify_transaction(test_context, tx, config.expected_error()) + .await; + if config.expected_error().is_some() { + return current_state.auction_state().clone(); + } + + AuctionState::Settled +} + +async fn create_settle_auction_none_cctp_shimless_accounts( + test_context: &mut ProgramTestContext, + testing_context: &TestingContext, + current_state: &TestingEngineState, + config: &SettleAuctionNoneInstructionConfig, +) -> SettleAuctionNoneCctpAccounts { + let payer = config + .payer_signer + .clone() + .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); + + let order_prepared_state = current_state.order_prepared().unwrap(); + let OrderPreparedState { + prepared_order_response_address, + prepared_custody_token, + base_fee_token: _, + prepared_by, + actor_enum: _, + } = *order_prepared_state; + + let checked_custodian = CheckedCustodian { + custodian: current_state.custodian_address().unwrap(), + }; + + let prepared_order_response_data = test_context + .banks_client + .get_account(prepared_order_response_address) + .await + .unwrap() + .unwrap() + .data; + let prepared_order = + PreparedOrderResponse::try_deserialize(&mut &prepared_order_response_data[..]).unwrap(); + let auction_seeds = &[ + Auction::SEED_PREFIX, + &prepared_order.seeds.fast_vaa_hash.as_ref(), + ]; + let (auction, _bump) = Pubkey::find_program_address( + auction_seeds, + &testing_context.get_matching_engine_program_id(), + ); + let (core_message, _bump) = Pubkey::find_program_address( + &[common::CORE_MESSAGE_SEED_PREFIX, &auction.as_ref()], + &testing_context.get_matching_engine_program_id(), + ); + + let (cctp_message, _bump) = Pubkey::find_program_address( + &[common::CCTP_MESSAGE_SEED_PREFIX, &auction.to_bytes()], + &testing_context.get_matching_engine_program_id(), + ); + let close_prepare_order_response = ClosePreparedOrderResponse { + by: prepared_by, + custody_token: prepared_custody_token, + order_response: prepared_order_response_address, + }; + let emitter_sequence = wormhole_svm_definitions::find_emitter_sequence_address( + &checked_custodian.custodian, + &wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, + ) + .0; + let wormhole_publish_message = WormholePublishMessage { + config: wormhole_svm_definitions::solana::CORE_BRIDGE_CONFIG, + emitter_sequence, + fee_collector: wormhole_svm_definitions::solana::CORE_BRIDGE_FEE_COLLECTOR, + core_bridge_program: wormhole_svm_definitions::solana::CORE_BRIDGE_PROGRAM_ID, + }; + + let cctp = create_cctp_deposit_for_burn(current_state, testing_context); + + let sysvars = RequiredSysvars { + clock: Clock::id(), + rent: Rent::id(), + }; + + let event_authority = Pubkey::find_program_address( + &[EVENT_AUTHORITY_SEED], + &testing_context.get_matching_engine_program_id(), + ) + .0; + + let spl_token_enum = current_state + .spl_token_enum() + .unwrap_or_else(|| SplTokenEnum::Usdc); + let fee_recipient_token = testing_context + .testing_actors + .fee_recipient + .token_account_address(&spl_token_enum) + .unwrap(); + + SettleAuctionNoneCctpAccounts { + payer: payer.pubkey(), + custodian: checked_custodian, + fee_recipient_token, + core_message, + cctp_message, + prepared: close_prepare_order_response, + auction, + wormhole: wormhole_publish_message, + cctp, + token_program: spl_token::ID, + system_program: solana_program::system_program::ID, + event_authority, + program: testing_context.get_matching_engine_program_id(), + sysvars, + } +} diff --git a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs index 54fd7a437..1f2a7d47f 100644 --- a/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/test_scenarios/settle_auction.rs @@ -239,6 +239,84 @@ pub async fn test_settle_auction_base_fee_token_not_best_offer_actor() { .await; } +/// Test settle auction none shim +#[tokio::test] +pub async fn test_settle_auction_none_shimful() { + let (mut test_context, prepared_order_state, testing_engine, _initial_balances) = + Box::pin(helpers::prepare_settle_auction_none_shimful()).await; + let instruction_triggers = vec![InstructionTrigger::SettleAuctionNoneShim( + SettleAuctionNoneInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; +} + +/// Test settle auction none shimless +#[tokio::test] +pub async fn test_settle_auction_none_shimless() { + let (mut test_context, prepared_order_state, testing_engine, _initial_balances) = + Box::pin(helpers::prepare_settle_auction_none_cctp_shimless()).await; + let instruction_triggers = vec![InstructionTrigger::SettleAuctionNoneShimless( + SettleAuctionNoneInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; +} + +/// Test that balance changes are comparable between shim and shimless +#[tokio::test] +pub async fn test_settle_auction_none_balance_changes_comparable() { + let balance_changes_shimful = { + let (mut test_context, prepared_order_state, testing_engine, initial_balances) = + Box::pin(helpers::prepare_settle_auction_none_shimful()).await; + let instruction_triggers = vec![InstructionTrigger::SettleAuctionNoneShim( + SettleAuctionNoneInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; + let final_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + BalanceChanges::from((&initial_balances, &final_balances)) + }; + let balance_changes_shimless = { + let (mut test_context, prepared_order_state, testing_engine, initial_balances) = + Box::pin(helpers::prepare_settle_auction_none_cctp_shimless()).await; + let instruction_triggers = vec![InstructionTrigger::SettleAuctionNoneShimless( + SettleAuctionNoneInstructionConfig::default(), + )]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; + let final_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + BalanceChanges::from((&initial_balances, &final_balances)) + }; + helpers::compare_balance_changes(&balance_changes_shimful, &balance_changes_shimless); +} + /* Sad path tests section @@ -322,10 +400,39 @@ pub async fn test_settle_auction_non_existent() { .await; } +/// Test cannot settle auction none if place initial offer is made +#[tokio::test] +pub async fn test_cannot_settle_auction_none_shim_after_place_initial_offer() { + let (mut test_context, prepared_order_state, testing_engine, _initial_balances) = + Box::pin(helpers::prepare_settle_auction_none_shimful()).await; + let instruction_triggers = vec![ + InstructionTrigger::PlaceInitialOfferShim(PlaceInitialOfferInstructionConfig::default()), + InstructionTrigger::SettleAuctionNoneShim(SettleAuctionNoneInstructionConfig { + expected_error: Some(ExpectedError { + instruction_index: 0, + error_code: 0, + error_string: "Account In Use".to_string(), + }), + ..SettleAuctionNoneInstructionConfig::default() + }), + ]; + testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(prepared_order_state), + ) + .await; +} + /* Helper code */ mod helpers { + use solana_program_test::ProgramTestContext; + + use crate::testing_engine::{setup::Balances, state::TestingEngineState}; + use super::*; pub async fn balance_changes_shim() -> BalanceChanges { @@ -527,4 +634,124 @@ mod helpers { "Solver 0 balance change should be the same for both shim and shimless" ); } + + /// Prepares testing engine state for settle auction none shimful + /// Returns: + /// - ProgramTestContext + /// - TestingEngineState + /// - TestingEngine + /// - Initial balances + pub async fn prepare_settle_auction_none_shimful() -> ( + ProgramTestContext, + TestingEngineState, + TestingEngine, + Balances, + ) { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (testing_context, mut test_context) = setup_environment( + ShimMode::VerifyAndPostSignature, + transfer_direction, + Some(vec![VaaArgs::default()]), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ]; + let initial_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let create_cctp_router_endpoints_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + + // This is just needed to get the router endpoint accounts when prepare order happens before place initial offer, it is not used for anything else + let fake_auction_accounts = AuctionAccounts::fake_auction_accounts( + &create_cctp_router_endpoints_state, + &testing_engine.testing_context, + ); + let instruction_triggers = vec![ + InstructionTrigger::InitializeFastMarketOrderShim( + InitializeFastMarketOrderShimInstructionConfig::default(), + ), + InstructionTrigger::PrepareOrderShim(PrepareOrderResponseInstructionConfig { + overwrite_auction_accounts: Some(fake_auction_accounts), + ..Default::default() + }), + ]; + let prepare_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(create_cctp_router_endpoints_state), + ) + .await; + ( + test_context, + prepare_order_state, + testing_engine, + initial_state_balances, + ) + } + + pub async fn prepare_settle_auction_none_cctp_shimless() -> ( + ProgramTestContext, + TestingEngineState, + TestingEngine, + Balances, + ) { + let transfer_direction = TransferDirection::FromEthereumToArbitrum; + let (testing_context, mut test_context) = setup_environment( + ShimMode::None, + transfer_direction, + Some(vec![VaaArgs { + post_vaa: true, + ..VaaArgs::default() + }]), + ) + .await; + let testing_engine = TestingEngine::new(testing_context).await; + + let instruction_triggers = vec![ + InstructionTrigger::InitializeProgram(InitializeInstructionConfig::default()), + InstructionTrigger::CreateCctpRouterEndpoints( + CreateCctpRouterEndpointsInstructionConfig::default(), + ), + ]; + let initial_state_balances = testing_engine + .testing_context + .get_balances(&mut test_context) + .await; + let create_cctp_router_endpoints_state = testing_engine + .execute(&mut test_context, instruction_triggers, None) + .await; + let fake_auction_accounts = AuctionAccounts::fake_auction_accounts( + &create_cctp_router_endpoints_state, + &testing_engine.testing_context, + ); + let instruction_triggers = vec![InstructionTrigger::PrepareOrderShimless( + PrepareOrderResponseInstructionConfig { + overwrite_auction_accounts: Some(fake_auction_accounts), + ..Default::default() + }, + )]; + let prepare_order_state = testing_engine + .execute( + &mut test_context, + instruction_triggers, + Some(create_cctp_router_endpoints_state), + ) + .await; + ( + test_context, + prepare_order_state, + testing_engine, + initial_state_balances, + ) + } } diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index fe61ab4ce..7c55f8bc7 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -400,6 +400,21 @@ impl InstructionConfig for ImproveOfferInstructionConfig { self.expected_log_messages.as_ref() } } +#[derive(Default)] +pub struct SettleAuctionNoneInstructionConfig { + pub payer_signer: Option>, + pub expected_error: Option, + pub expected_log_messages: Option>, +} + +impl InstructionConfig for SettleAuctionNoneInstructionConfig { + fn expected_error(&self) -> Option<&ExpectedError> { + self.expected_error.as_ref() + } + fn expected_log_messages(&self) -> Option<&Vec> { + self.expected_log_messages.as_ref() + } +} pub struct VerifyBalancesConfig { pub previous_state_balances: Balances, diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs index 8484e9ff0..d194ab75c 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/engine.rs @@ -44,6 +44,7 @@ use crate::shimful::verify_shim::create_guardian_signatures; use crate::shimless; use crate::shimless::initialize::initialize_program; use crate::testing_engine::setup::ShimMode; +use crate::utils::auction::AuctionState; use crate::utils::token_account::SplTokenEnum; use crate::utils::vaa::TestVaaPairs; use crate::utils::{auction::AuctionAccounts, router::create_all_router_endpoints_test}; @@ -63,6 +64,8 @@ pub enum InstructionTrigger { PrepareOrderShim(PrepareOrderResponseInstructionConfig), SettleAuction(SettleAuctionInstructionConfig), CloseFastMarketOrderShim(CloseFastMarketOrderShimInstructionConfig), + SettleAuctionNoneShim(SettleAuctionNoneInstructionConfig), + SettleAuctionNoneShimless(SettleAuctionNoneInstructionConfig), } pub enum VerificationTrigger { @@ -180,6 +183,8 @@ impl InstructionConfig for InstructionTrigger { Self::PrepareOrderShim(config) => config.expected_error(), Self::SettleAuction(config) => config.expected_error(), Self::CloseFastMarketOrderShim(config) => config.expected_error(), + Self::SettleAuctionNoneShim(config) => config.expected_error(), + Self::SettleAuctionNoneShimless(config) => config.expected_error(), } } fn expected_log_messages(&self) -> Option<&Vec> { @@ -197,6 +202,8 @@ impl InstructionConfig for InstructionTrigger { Self::PrepareOrderShimless(config) => config.expected_log_messages(), Self::SettleAuction(config) => config.expected_log_messages(), Self::CloseFastMarketOrderShim(config) => config.expected_log_messages(), + Self::SettleAuctionNoneShim(config) => config.expected_log_messages(), + Self::SettleAuctionNoneShimless(config) => config.expected_log_messages(), } } } @@ -361,6 +368,14 @@ impl TestingEngine { self.settle_auction(test_context, current_state, config) .await } + InstructionTrigger::SettleAuctionNoneShim(ref config) => { + self.settle_auction_none_shim(test_context, current_state, config) + .await + } + InstructionTrigger::SettleAuctionNoneShimless(ref config) => { + self.settle_auction_none_shimless(test_context, current_state, config) + .await + } }, ExecutionTrigger::Verification(trigger) => match **trigger { VerificationTrigger::VerifyAuctionState(expected_to_succeed) => { @@ -701,6 +716,63 @@ impl TestingEngine { ) .await } + /// Instruction trigger function for settling an auction none shim + async fn settle_auction_none_shim( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &SettleAuctionNoneInstructionConfig, + ) -> TestingEngineState { + let auction_state = shimful::shims_settle_auction_none_cctp::settle_auction_none_shimful( + &self.testing_context, + test_context, + current_state, + config, + ) + .await; + match auction_state { + AuctionState::Settled(auction_state) => TestingEngineState::AuctionSettled { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + auction_state: AuctionState::Settled(auction_state.clone()), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: current_state.order_prepared().unwrap().clone(), + auction_accounts: current_state.auction_accounts().cloned(), + order_executed: current_state.order_executed().cloned(), + }, + _ => current_state.clone(), + } + } + + /// Instruction trigger function for settling an auction none shimless + async fn settle_auction_none_shimless( + &self, + test_context: &mut ProgramTestContext, + current_state: &TestingEngineState, + config: &SettleAuctionNoneInstructionConfig, + ) -> TestingEngineState { + let auction_state = shimless::settle_auction_none_cctp::settle_auction_none_shimless( + &self.testing_context, + current_state, + test_context, + config, + ) + .await; + match auction_state { + AuctionState::Settled(auction_state) => TestingEngineState::AuctionSettled { + base: current_state.base().clone(), + initialized: current_state.initialized().unwrap().clone(), + router_endpoints: current_state.router_endpoints().unwrap().clone(), + auction_state: AuctionState::Settled(auction_state.clone()), + fast_market_order: current_state.fast_market_order().cloned(), + order_prepared: current_state.order_prepared().unwrap().clone(), + auction_accounts: current_state.auction_accounts().cloned(), + order_executed: current_state.order_executed().cloned(), + }, + _ => current_state.clone(), + } + } // -------------------------------------------------------------------------------------------- // Verification trigger functions diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs index f55037aed..af18ffc89 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/state.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/state.rs @@ -88,6 +88,7 @@ pub struct OrderPreparedState { pub prepared_custody_token: Pubkey, pub base_fee_token: Pubkey, pub actor_enum: TestingActorEnum, + pub prepared_by: Pubkey, } #[derive(Clone)] diff --git a/solana/programs/matching-engine/src/fallback/processor/mod.rs b/solana/programs/matching-engine/src/fallback/processor/mod.rs index a829f93e3..3d1d7df7e 100644 --- a/solana/programs/matching-engine/src/fallback/processor/mod.rs +++ b/solana/programs/matching-engine/src/fallback/processor/mod.rs @@ -8,5 +8,5 @@ pub mod initialize_fast_market_order; pub mod place_initial_offer; pub mod prepare_order_response; pub mod process_instruction; - +pub mod settle_auction_none_cctp; pub use process_instruction::*; diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index a08692b24..ee6124964 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -8,6 +8,7 @@ use super::place_initial_offer::PlaceInitialOfferCctpShimData; use super::prepare_order_response::{ prepare_order_response_cctp_shim, PrepareOrderResponseCctpShimData, }; +use super::settle_auction_none_cctp::SettleAuctionNoneCctpShimData; const SELECTOR_SIZE: usize = 8; @@ -22,6 +23,8 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { make_anchor_discriminator(b"global:execute_order_cctp_shim"); pub const PREPARE_ORDER_RESPONSE_CCTP_SHIM_SELECTOR: [u8; SELECTOR_SIZE] = make_anchor_discriminator(b"global:prepare_order_response_cctp_shim"); + pub const SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR: [u8; SELECTOR_SIZE] = + make_anchor_discriminator(b"global:settle_auction_none_cctp_shim"); } pub enum FallbackMatchingEngineInstruction<'ix> { @@ -31,6 +34,7 @@ pub enum FallbackMatchingEngineInstruction<'ix> { PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), + SettleAuctionNoneCctpShim(&'ix SettleAuctionNoneCctpShimData), } pub fn process_instruction( @@ -61,6 +65,9 @@ pub fn process_instruction( FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { prepare_order_response_cctp_shim(accounts, data) } + FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(data) => { + super::settle_auction_none_cctp::process(accounts, data) + } } } @@ -92,6 +99,11 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { .ok() .map(Self::PrepareOrderResponseCctpShim) } + FallbackMatchingEngineInstruction::SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR => { + bytemuck::try_from_bytes(&instruction_data[SELECTOR_SIZE..]) + .ok() + .map(Self::SettleAuctionNoneCctpShim) + } _ => None, } } @@ -140,6 +152,18 @@ impl FallbackMatchingEngineInstruction<'_> { ); out.extend(tmp_data); + out + } + FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(data) => { + let mut out = Vec::with_capacity( + SELECTOR_SIZE + std::mem::size_of::(), + ); + + out.extend_from_slice( + &FallbackMatchingEngineInstruction::SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR, + ); + out.extend_from_slice(bytemuck::bytes_of(*data)); + out } } diff --git a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs new file mode 100644 index 000000000..6ab16ba76 --- /dev/null +++ b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs @@ -0,0 +1,493 @@ +use crate::ID; +use anchor_lang::{prelude::*, Discriminator}; +use anchor_spl::token::{spl_token, TokenAccount}; +use bytemuck::{Pod, Zeroable}; +use common::wormhole_io::TypePrefixedPayload; +use solana_program::program::invoke_signed_unchecked; + +use crate::{ + error::MatchingEngineError, + events::AuctionSettled, + processor::SettledNone, + state::{Auction, AuctionStatus, Custodian, MessageProtocol, PreparedOrderResponse}, +}; + +use super::{ + burn_and_post::{burn_and_post, PostMessageAccounts}, + helpers::{create_account_reliably, require_min_account_infos_len}, +}; + +#[derive(Debug, Copy, Clone, Pod, Zeroable)] +#[repr(C)] +pub struct SettleAuctionNoneCctpShimData { + pub cctp_message_bump: u8, + pub auction_bump: u8, +} + +pub struct SettleAuctionNoneCctpShimAccounts<'ix> { + /// Payer of the account + pub payer: &'ix Pubkey, // 0 + /// Post shim message account + pub post_shim_message: &'ix Pubkey, // 1 + /// Core bridge emitter sequence account + pub core_bridge_emitter_sequence: &'ix Pubkey, // 2 + /// Post message shim event authority + pub post_message_shim_event_authority: &'ix Pubkey, // 3 + /// Post message shim program + pub post_message_shim_program: &'ix Pubkey, // 4 + /// Cctp message CHECK: Seeds must be \["cctp-msg", auction.key().as_ref()\]. + pub cctp_message: &'ix Pubkey, // 5 + /// Custodian account + pub custodian: &'ix Pubkey, // 6 + /// Fee recipient token + pub fee_recipient_token: &'ix Pubkey, // 7 + /// Closed prepared order response actor (closed_by) + pub closed_prepared_order_response_actor: &'ix Pubkey, // 8 + /// Closed prepared order response + pub closed_prepared_order_response: &'ix Pubkey, // 9 + /// Closed prepared order response custody token + pub closed_prepared_order_response_custody_token: &'ix Pubkey, // 10 + /// Auction account CHECK: Init if needed, Seeds must be \["auction", prepared.order_response.seeds.fast_vaa_hash.as_ref()\]. + pub auction: &'ix Pubkey, // 11 + /// Cctp mint (must be USDC mint) + pub cctp_mint: &'ix Pubkey, // 12 + /// Cctp token messenger minter sender authority + pub cctp_token_messenger_minter_sender_authority: &'ix Pubkey, // 13 + /// Cctp message transmitter config + pub cctp_message_transmitter_config: &'ix Pubkey, // 14 + /// Cctp token messenger + pub cctp_token_messenger: &'ix Pubkey, // 15 + /// Cctp remote token messenger + pub cctp_remote_token_messenger: &'ix Pubkey, // 16 + /// Cctp token minter + pub cctp_token_minter: &'ix Pubkey, // 17 + /// Cctp local token + pub cctp_local_token: &'ix Pubkey, // 18 + /// Cctp token messenger minter event authority + pub cctp_token_messenger_minter_event_authority: &'ix Pubkey, // 19 + /// Cctp token messenger minter program + pub cctp_token_messenger_minter_program: &'ix Pubkey, // 20 + /// Cctp message transmitter program + pub cctp_message_transmitter_program: &'ix Pubkey, // 21 + /// Core bridge program + pub core_bridge_program: &'ix Pubkey, // 22 + /// Core bridge fee collector + pub core_bridge_fee_collector: &'ix Pubkey, // 23 + /// Core bridge config + pub core_bridge_config: &'ix Pubkey, // 24 + /// Token program + pub token_program: &'ix Pubkey, // 25 + /// System program + pub system_program: &'ix Pubkey, // 26 + /// Clock + pub clock: &'ix Pubkey, // 27 + /// Rent + pub rent: &'ix Pubkey, // 28 +} + +impl<'ix> SettleAuctionNoneCctpShimAccounts<'ix> { + pub fn to_account_metas(&self) -> Vec { + vec![ + AccountMeta::new_readonly(*self.payer, true), // 0 + AccountMeta::new(*self.post_shim_message, false), // 1 + AccountMeta::new(*self.core_bridge_emitter_sequence, false), // 2 + AccountMeta::new_readonly(*self.post_message_shim_event_authority, false), // 3 + AccountMeta::new_readonly(*self.post_message_shim_program, false), // 4 + AccountMeta::new(*self.cctp_message, false), // 5 + AccountMeta::new(*self.custodian, false), // 6 + AccountMeta::new(*self.fee_recipient_token, false), // 7 + AccountMeta::new(*self.closed_prepared_order_response_actor, false), // 8 + AccountMeta::new_readonly(*self.closed_prepared_order_response, false), // 9 + AccountMeta::new(*self.closed_prepared_order_response_custody_token, false), // 10 + AccountMeta::new(*self.auction, false), // 11 + AccountMeta::new(*self.cctp_mint, false), // 12 + AccountMeta::new_readonly(*self.cctp_token_messenger_minter_sender_authority, false), // 13 + AccountMeta::new(*self.cctp_message_transmitter_config, false), // 14 + AccountMeta::new_readonly(*self.cctp_token_messenger, false), // 15 + AccountMeta::new_readonly(*self.cctp_remote_token_messenger, false), // 16 + AccountMeta::new(*self.cctp_token_minter, false), // 17 + AccountMeta::new(*self.cctp_local_token, false), // 18 + AccountMeta::new_readonly(*self.cctp_token_messenger_minter_event_authority, false), // 19 + AccountMeta::new_readonly(*self.cctp_token_messenger_minter_program, false), // 20 + AccountMeta::new_readonly(*self.cctp_message_transmitter_program, false), // 21 + AccountMeta::new_readonly(*self.core_bridge_program, false), // 22 + AccountMeta::new(*self.core_bridge_fee_collector, false), // 23 + AccountMeta::new(*self.core_bridge_config, false), // 24 + AccountMeta::new_readonly(*self.token_program, false), // 25 + AccountMeta::new_readonly(*self.system_program, false), // 26 + AccountMeta::new_readonly(*self.clock, false), // 27 + AccountMeta::new_readonly(*self.rent, false), // 28 + ] + } +} + +pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) -> Result<()> { + let program_id = &crate::ID; + require_min_account_infos_len(accounts, 29)?; + let payer = &accounts[0]; + let post_shim_message = &accounts[1]; + let core_bridge_emitter_sequence = &accounts[2]; + let _post_message_shim_event_authority = &accounts[3]; + let _post_message_shim_program = &accounts[4]; + let cctp_message = &accounts[5]; + let custodian = &accounts[6]; + let fee_recipient_token = &accounts[7]; // Who is this? + let closed_prepared_order_response_actor = &accounts[8]; + let closed_prepared_order_response = &accounts[9]; + let closed_prepared_order_response_custody_token = &accounts[10]; + let auction = &accounts[11]; // Will be created here + let cctp_mint = &accounts[12]; + let cctp_token_messenger_minter_sender_authority = &accounts[13]; + let cctp_message_transmitter_config = &accounts[14]; + let cctp_token_messenger = &accounts[15]; + let cctp_remote_token_messenger = &accounts[16]; + let cctp_token_minter = &accounts[17]; + let cctp_local_token = &accounts[18]; + let cctp_token_messenger_minter_event_authority = &accounts[19]; + let cctp_token_messenger_minter_program = &accounts[20]; + let cctp_message_transmitter_program = &accounts[21]; + let _core_bridge_program = &accounts[22]; + let _core_bridge_fee_collector = &accounts[23]; + let _core_bridge_config = &accounts[24]; + let token_program = &accounts[25]; + let system_program = &accounts[26]; + let _clock = &accounts[27]; + let _rent = &accounts[28]; + + let mut prepared_order_response_account = PreparedOrderResponse::try_deserialize( + &mut &closed_prepared_order_response.data.borrow_mut()[..], + )?; + let fee_recipient_token_account = + &TokenAccount::try_deserialize(&mut &fee_recipient_token.data.borrow_mut()[..])?; + let prepared_custody_token_account = &TokenAccount::try_deserialize( + &mut &closed_prepared_order_response_custody_token + .data + .borrow_mut()[..], + )?; + + let to_router_endpoint = prepared_order_response_account.to_endpoint; + let destination_cctp_domain = match to_router_endpoint.protocol { + MessageProtocol::Cctp { domain } => domain, + _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()), + }; + + let auction_key = auction.key(); + + // Start of checks + // ------------------------------------------------------------------------------------------------ + + // Check cctp message is writable + if !cctp_message.is_writable { + msg!("Cctp message is not writable"); + return Err(MatchingEngineError::AccountNotWritable.into()) + .map_err(|e: Error| e.with_account_name("cctp_message")); + } + + // Check cctp message seeds are valid + let cctp_message_seeds = [ + common::CCTP_MESSAGE_SEED_PREFIX, + auction_key.as_ref(), + &[data.cctp_message_bump], + ]; + + let cctp_message_pda = Pubkey::create_program_address(&cctp_message_seeds, &ID) + .map_err(|_| MatchingEngineError::InvalidPda)?; + if cctp_message_pda != cctp_message.key() { + msg!("Cctp message seeds are invalid"); + return Err(ErrorCode::ConstraintSeeds.into()) + .map_err(|e: Error| e.with_pubkeys((cctp_message_pda, cctp_message.key()))); + }; + // Check custodian owner is the matching engine program and that it deserializes into a checked custodian + require_eq!(custodian.owner, &ID); + let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow_mut()[..])?; + // Check that the fee recipient token is the custodian's fee recipient token + require_eq!( + fee_recipient_token.key(), + checked_custodian.fee_recipient_token + ); + + // Check seeds of prepared order response are valid + let prepared_order_response_pda = Pubkey::create_program_address( + &[ + PreparedOrderResponse::SEED_PREFIX, + prepared_order_response_account.seeds.fast_vaa_hash.as_ref(), + &[prepared_order_response_account.seeds.bump], + ], + program_id, + ) + .map_err(|_| MatchingEngineError::InvalidPda)?; + if prepared_order_response_pda != closed_prepared_order_response.key() { + msg!("Prepared order response seeds are invalid"); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys(( + prepared_order_response_pda, + closed_prepared_order_response.key(), + )) + }); + }; + // Check seeds of prepared custody token are valid + { + let (prepared_custody_token_pda, _) = Pubkey::find_program_address( + &[ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + closed_prepared_order_response.key().as_ref(), + ], + program_id, + ); + if prepared_custody_token_pda != closed_prepared_order_response_custody_token.key() { + msg!("Prepared custody token seeds are invalid"); + return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { + e.with_pubkeys(( + prepared_custody_token_pda, + closed_prepared_order_response_custody_token.key(), + )) + }); + }; + } + + // Check prepared by is the same as the prepared by in the accounts + require_eq!( + prepared_order_response_account.prepared_by, + closed_prepared_order_response_actor.key() + ); + + // Check that custody token is a token account + let _checked_prepared_custody_token = Box::new(TokenAccount::try_deserialize( + &mut &closed_prepared_order_response_custody_token + .data + .borrow_mut()[..], + )?); + // Check seeds of auction are valid + let auction_seeds = [ + Auction::SEED_PREFIX, + prepared_order_response_account.seeds.fast_vaa_hash.as_ref(), + &[data.auction_bump], + ]; + let auction_pda = Pubkey::create_program_address(&auction_seeds, program_id) + .map_err(|_| MatchingEngineError::InvalidPda)?; + if auction_pda != auction.key() { + return Err(MatchingEngineError::InvalidPda.into()); + } + + // End of checks + // ------------------------------------------------------------------------------------------------ + + // Begin of initialisation of auction account + // ------------------------------------------------------------------------------------------------ + let auction_space = 8 + Auction::INIT_SPACE_NO_AUCTION; + + let auction_signer_seeds = &[&auction_seeds[..]]; + create_account_reliably( + &payer.key(), + &auction.key(), + auction.lamports(), + auction_space, + accounts, + program_id, + auction_signer_seeds, + )?; + // Borrow the account data mutably + let mut auction_data = auction + .try_borrow_mut_data() + .map_err(|_| MatchingEngineError::AccountNotWritable)?; + // Write the discriminator to the first 8 bytes + let discriminator = Auction::discriminator(); + auction_data[0..8].copy_from_slice(&discriminator); + let mut auction_to_write = + prepared_order_response_account.new_auction_placeholder(data.auction_bump); + let prepare_none_and_settle_fill_pubkeys = PrepareNoneAndSettleFillPubkeys { + prepared_order_response_key: &closed_prepared_order_response.key(), + prepared_order_response_custody_token: &closed_prepared_order_response_custody_token.key(), + fee_recipient_token_key: &fee_recipient_token.key(), + custodian_key: &custodian.key(), + }; + let SettledNone { + user_amount, + fill, + auction_settled_event: _, + } = prepare_none_and_settle_fill( + prepare_none_and_settle_fill_pubkeys, + &mut prepared_order_response_account, + &mut auction_to_write, + fee_recipient_token_account, + prepared_custody_token_account, + accounts, + )?; + let auction_bytes = auction_to_write + .try_to_vec() + .map_err(|_| MatchingEngineError::BorshDeserializationError)?; + auction_data[8..8_usize.saturating_add(auction_bytes.len())].copy_from_slice(&auction_bytes); + // ------------------------------------------------------------------------------------------------ + // End of initialisation of auction account + + // Begin of burning and posting the message + // ------------------------------------------------------------------------------------------------ + let post_message_accounts = PostMessageAccounts { + emitter: custodian.key, + payer: payer.key, + message: post_shim_message.key, + sequence: core_bridge_emitter_sequence.key, + }; + burn_and_post( + CpiContext::new_with_signer( + cctp_token_messenger_minter_program.to_account_info(), + common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { + burn_token_owner: custodian.to_account_info(), + payer: payer.to_account_info(), + token_messenger_minter_sender_authority: + cctp_token_messenger_minter_sender_authority.to_account_info(), + burn_token: closed_prepared_order_response_custody_token.to_account_info(), + message_transmitter_config: cctp_message_transmitter_config.to_account_info(), + token_messenger: cctp_token_messenger.to_account_info(), + remote_token_messenger: cctp_remote_token_messenger.to_account_info(), + token_minter: cctp_token_minter.to_account_info(), + local_token: cctp_local_token.to_account_info(), + mint: cctp_mint.to_account_info(), + cctp_message: cctp_message.to_account_info(), + message_transmitter_program: cctp_message_transmitter_program.to_account_info(), + token_messenger_minter_program: cctp_token_messenger_minter_program + .to_account_info(), + token_program: token_program.to_account_info(), + system_program: system_program.to_account_info(), + event_authority: cctp_token_messenger_minter_event_authority.to_account_info(), + }, + &[ + Custodian::SIGNER_SEEDS, + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + auction.key().as_ref(), + &[data.cctp_message_bump], + ], + ], + ), + common::wormhole_cctp_solana::cpi::BurnAndPublishArgs { + burn_source: None, + destination_caller: to_router_endpoint.address, + destination_cctp_domain, + amount: user_amount, + mint_recipient: to_router_endpoint.mint_recipient, + wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, + payload: fill.to_vec(), + }, + post_message_accounts, + accounts, + )?; + // ------------------------------------------------------------------------------------------------ + // End of burning and posting the message + + // Begin of closing the prepared order response + // ------------------------------------------------------------------------------------------------ + let close_token_account_ix = spl_token::instruction::close_account( + &spl_token::ID, + &closed_prepared_order_response_custody_token.key(), + &closed_prepared_order_response_actor.key(), + &custodian.key(), + &[], + )?; + invoke_signed_unchecked( + &close_token_account_ix, + accounts, + &[&Custodian::SIGNER_SEEDS], + )?; + // ------------------------------------------------------------------------------------------------ + // End of closing the prepared order response + + Ok(()) +} + +struct PrepareNoneAndSettleFillPubkeys<'ix> { + prepared_order_response_key: &'ix Pubkey, + prepared_order_response_custody_token: &'ix Pubkey, + fee_recipient_token_key: &'ix Pubkey, + custodian_key: &'ix Pubkey, +} + +// Rewrite of settle_none_and_prepare_fill +fn prepare_none_and_settle_fill<'ix>( + pubkeys: PrepareNoneAndSettleFillPubkeys<'ix>, + prepared_order_response: &'ix mut PreparedOrderResponse, + auction: &mut Auction, + fee_recipient_token: &'ix TokenAccount, + prepared_custody_token: &'ix TokenAccount, + accounts: &[AccountInfo], +) -> Result { + let PrepareNoneAndSettleFillPubkeys { + prepared_order_response_key, + prepared_order_response_custody_token, + fee_recipient_token_key, + custodian_key, + } = pubkeys; + let prepared_order_response_signer_seeds = &[ + PreparedOrderResponse::SEED_PREFIX, + prepared_order_response.seeds.fast_vaa_hash.as_ref(), + &[prepared_order_response.seeds.bump], + ]; + // Pay the `fee_recipient` the base fee and init auction fee. This ensures that the protocol + // relayer is paid for relaying slow VAAs (which requires posting the fast order VAA) that do + // not have an associated auction. + let fee = prepared_order_response + .base_fee + .saturating_add(prepared_order_response.init_auction_fee); + + let transfer_ix = spl_token::instruction::transfer( + &spl_token::ID, + prepared_order_response_custody_token, + fee_recipient_token_key, + prepared_order_response_key, + &[], + fee, + )?; + + invoke_signed_unchecked( + &transfer_ix, + accounts, + &[prepared_order_response_signer_seeds], + )?; + + // Set authority instruction + let set_authority_ix = spl_token::instruction::set_authority( + &spl_token::ID, + prepared_order_response_custody_token, + Some(custodian_key), + spl_token::instruction::AuthorityType::AccountOwner, + prepared_order_response_key, + &[], + )?; + + invoke_signed_unchecked( + &set_authority_ix, + accounts, + &[prepared_order_response_signer_seeds], + )?; + + auction.status = AuctionStatus::Settled { + fee, + total_penalty: None, + }; + + let auction_settled_event = AuctionSettled { + fast_vaa_hash: auction.vaa_hash, + best_offer_token: Default::default(), + base_fee_token: crate::events::SettledTokenAccountInfo { + key: *fee_recipient_token_key, + balance_after: fee_recipient_token.amount.saturating_add(fee), + } + .into(), + with_execute: auction.target_protocol.into(), + }; + // TryInto is safe to unwrap here because the redeemer message had to have been able to fit in + // the prepared order response account (so it would not have exceed u32::MAX). + let redeemer_message = std::mem::take(&mut prepared_order_response.redeemer_message) + .try_into() + .unwrap(); + Ok(SettledNone { + user_amount: prepared_custody_token.amount.saturating_sub(fee), + fill: common::messages::Fill { + source_chain: prepared_order_response.source_chain, + order_sender: prepared_order_response.sender, + redeemer: prepared_order_response.redeemer, + redeemer_message, + }, + auction_settled_event, + }) +} diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs index 9c8233cc7..0b1ac5076 100644 --- a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs +++ b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs @@ -22,10 +22,10 @@ struct SettleNoneAndPrepareFill<'ctx, 'info> { token_program: &'ctx Program<'info, token::Token>, } -struct SettledNone { - user_amount: u64, - fill: Fill, - auction_settled_event: AuctionSettled, +pub struct SettledNone { + pub user_amount: u64, + pub fill: Fill, + pub auction_settled_event: AuctionSettled, } fn settle_none_and_prepare_fill(accounts: SettleNoneAndPrepareFill<'_, '_>) -> Result { From 253c5731b7210aa70a5474734ad7c98586933511 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 14 May 2025 16:27:09 +0100 Subject: [PATCH 108/112] creating a helper function for settle_none_prepare_fill --- .../shimful/shims_settle_auction_none_cctp.rs | 26 +- solana/programs/matching-engine/src/error.rs | 1 + .../processor/settle_auction_none_cctp.rs | 544 ++++++++---------- .../src/processor/auction/settle/none/cctp.rs | 23 +- .../processor/auction/settle/none/local.rs | 23 +- .../src/processor/auction/settle/none/mod.rs | 88 +-- 6 files changed, 321 insertions(+), 384 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs index 289e3b528..e9bb14cf7 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs @@ -1,15 +1,11 @@ use anchor_lang::prelude::*; use anchor_spl::token::spl_token; use matching_engine::{ - fallback::{ - settle_auction_none_cctp::{ - SettleAuctionNoneCctpShimAccounts, SettleAuctionNoneCctpShimData, - }, - FallbackMatchingEngineInstruction, + fallback::settle_auction_none_cctp::{ + SettleAuctionNoneCctpShim, SettleAuctionNoneCctpShimAccounts, SettleAuctionNoneCctpShimData, }, state::Auction, }; -use solana_program::instruction::Instruction; use solana_program_test::ProgramTestContext; use solana_sdk::{signature::Signer, sysvar::SysvarId, transaction::Transaction}; use wormhole_svm_definitions::solana::{ @@ -30,22 +26,6 @@ use crate::{ use super::shims_execute_order::{create_cctp_accounts, CctpAccounts}; -pub struct SettleAuctionNoneCctpShim<'ix> { - pub program_id: &'ix Pubkey, - pub accounts: SettleAuctionNoneCctpShimAccounts<'ix>, - pub data: SettleAuctionNoneCctpShimData, -} - -impl SettleAuctionNoneCctpShim<'_> { - pub fn instruction(self) -> Instruction { - Instruction { - program_id: *self.program_id, - accounts: self.accounts.to_account_metas(), - data: FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(&self.data).to_vec(), - } - } -} - pub async fn settle_auction_none_shimful( testing_context: &TestingContext, test_context: &mut ProgramTestContext, @@ -170,7 +150,7 @@ fn create_settle_auction_none_cctp_shimful_accounts( let OrderPreparedState { prepared_order_response_address, prepared_custody_token, - base_fee_token, + base_fee_token: _, actor_enum: _, prepared_by, } = *order_prepared_state; diff --git a/solana/programs/matching-engine/src/error.rs b/solana/programs/matching-engine/src/error.rs index 2a168bf3d..ab56bae8e 100644 --- a/solana/programs/matching-engine/src/error.rs +++ b/solana/programs/matching-engine/src/error.rs @@ -86,6 +86,7 @@ pub enum MatchingEngineError { BaseFeeTokenRequired = 0x43e, CannotCloseAuctionYet = 0x500, + InvalidFeeRecipientToken = 0x501, AuctionHistoryNotFull = 0x502, AuctionHistoryFull = 0x504, diff --git a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs index 6ab16ba76..113233d6b 100644 --- a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs +++ b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs @@ -1,20 +1,24 @@ -use crate::ID; -use anchor_lang::{prelude::*, Discriminator}; +use crate::{ + processor::{settle_none_and_prepare_fill, SettleNoneAndPrepareFill}, + ID, +}; +use anchor_lang::prelude::*; use anchor_spl::token::{spl_token, TokenAccount}; use bytemuck::{Pod, Zeroable}; use common::wormhole_io::TypePrefixedPayload; +use solana_program::instruction::Instruction; use solana_program::program::invoke_signed_unchecked; use crate::{ error::MatchingEngineError, - events::AuctionSettled, processor::SettledNone, - state::{Auction, AuctionStatus, Custodian, MessageProtocol, PreparedOrderResponse}, + state::{Auction, Custodian, MessageProtocol, PreparedOrderResponse}, }; use super::{ burn_and_post::{burn_and_post, PostMessageAccounts}, helpers::{create_account_reliably, require_min_account_infos_len}, + FallbackMatchingEngineInstruction, }; #[derive(Debug, Copy, Clone, Pod, Zeroable)] @@ -35,20 +39,20 @@ pub struct SettleAuctionNoneCctpShimAccounts<'ix> { pub post_message_shim_event_authority: &'ix Pubkey, // 3 /// Post message shim program pub post_message_shim_program: &'ix Pubkey, // 4 - /// Cctp message CHECK: Seeds must be \["cctp-msg", auction.key().as_ref()\]. - pub cctp_message: &'ix Pubkey, // 5 /// Custodian account - pub custodian: &'ix Pubkey, // 6 + pub custodian: &'ix Pubkey, // 5 /// Fee recipient token - pub fee_recipient_token: &'ix Pubkey, // 7 + pub fee_recipient_token: &'ix Pubkey, // 6 + /// Closed prepared order response + pub closed_prepared_order_response: &'ix Pubkey, // 7 /// Closed prepared order response actor (closed_by) pub closed_prepared_order_response_actor: &'ix Pubkey, // 8 - /// Closed prepared order response - pub closed_prepared_order_response: &'ix Pubkey, // 9 /// Closed prepared order response custody token - pub closed_prepared_order_response_custody_token: &'ix Pubkey, // 10 + pub closed_prepared_order_response_custody_token: &'ix Pubkey, // 9 /// Auction account CHECK: Init if needed, Seeds must be \["auction", prepared.order_response.seeds.fast_vaa_hash.as_ref()\]. - pub auction: &'ix Pubkey, // 11 + pub auction: &'ix Pubkey, // 10 + /// Cctp message CHECK: Seeds must be \["cctp-msg", auction.key().as_ref()\]. + pub cctp_message: &'ix Pubkey, // 11 /// Cctp mint (must be USDC mint) pub cctp_mint: &'ix Pubkey, // 12 /// Cctp token messenger minter sender authority @@ -85,258 +89,285 @@ pub struct SettleAuctionNoneCctpShimAccounts<'ix> { pub rent: &'ix Pubkey, // 28 } -impl<'ix> SettleAuctionNoneCctpShimAccounts<'ix> { - pub fn to_account_metas(&self) -> Vec { - vec![ - AccountMeta::new_readonly(*self.payer, true), // 0 - AccountMeta::new(*self.post_shim_message, false), // 1 - AccountMeta::new(*self.core_bridge_emitter_sequence, false), // 2 - AccountMeta::new_readonly(*self.post_message_shim_event_authority, false), // 3 - AccountMeta::new_readonly(*self.post_message_shim_program, false), // 4 - AccountMeta::new(*self.cctp_message, false), // 5 - AccountMeta::new(*self.custodian, false), // 6 - AccountMeta::new(*self.fee_recipient_token, false), // 7 - AccountMeta::new(*self.closed_prepared_order_response_actor, false), // 8 - AccountMeta::new_readonly(*self.closed_prepared_order_response, false), // 9 - AccountMeta::new(*self.closed_prepared_order_response_custody_token, false), // 10 - AccountMeta::new(*self.auction, false), // 11 - AccountMeta::new(*self.cctp_mint, false), // 12 - AccountMeta::new_readonly(*self.cctp_token_messenger_minter_sender_authority, false), // 13 - AccountMeta::new(*self.cctp_message_transmitter_config, false), // 14 - AccountMeta::new_readonly(*self.cctp_token_messenger, false), // 15 - AccountMeta::new_readonly(*self.cctp_remote_token_messenger, false), // 16 - AccountMeta::new(*self.cctp_token_minter, false), // 17 - AccountMeta::new(*self.cctp_local_token, false), // 18 - AccountMeta::new_readonly(*self.cctp_token_messenger_minter_event_authority, false), // 19 - AccountMeta::new_readonly(*self.cctp_token_messenger_minter_program, false), // 20 - AccountMeta::new_readonly(*self.cctp_message_transmitter_program, false), // 21 - AccountMeta::new_readonly(*self.core_bridge_program, false), // 22 - AccountMeta::new(*self.core_bridge_fee_collector, false), // 23 - AccountMeta::new(*self.core_bridge_config, false), // 24 - AccountMeta::new_readonly(*self.token_program, false), // 25 - AccountMeta::new_readonly(*self.system_program, false), // 26 - AccountMeta::new_readonly(*self.clock, false), // 27 - AccountMeta::new_readonly(*self.rent, false), // 28 - ] +pub struct SettleAuctionNoneCctpShim<'ix> { + pub program_id: &'ix Pubkey, + pub accounts: SettleAuctionNoneCctpShimAccounts<'ix>, + pub data: SettleAuctionNoneCctpShimData, +} + +impl<'ix> SettleAuctionNoneCctpShim<'ix> { + pub fn instruction(self) -> Instruction { + let SettleAuctionNoneCctpShimAccounts { + payer, + post_shim_message, + core_bridge_emitter_sequence, + post_message_shim_event_authority, + post_message_shim_program, + cctp_message, + custodian, + fee_recipient_token, + closed_prepared_order_response, + closed_prepared_order_response_actor, + closed_prepared_order_response_custody_token, + auction, + cctp_mint, + cctp_token_messenger_minter_sender_authority, + cctp_message_transmitter_config, + cctp_token_messenger, + cctp_remote_token_messenger, + cctp_token_minter, + cctp_local_token, + cctp_token_messenger_minter_event_authority, + cctp_token_messenger_minter_program, + cctp_message_transmitter_program, + core_bridge_program, + core_bridge_fee_collector, + core_bridge_config, + token_program, + system_program, + clock, + rent, + } = self.accounts; + Instruction { + program_id: *self.program_id, + accounts: vec![ + AccountMeta::new_readonly(*payer, true), // 0 + AccountMeta::new(*post_shim_message, false), // 1 + AccountMeta::new(*core_bridge_emitter_sequence, false), // 2 + AccountMeta::new_readonly(*post_message_shim_event_authority, false), // 3 + AccountMeta::new_readonly(*post_message_shim_program, false), // 4 + AccountMeta::new(*custodian, false), // 5 + AccountMeta::new(*fee_recipient_token, false), // 6 + AccountMeta::new_readonly(*closed_prepared_order_response, false), // 7 + AccountMeta::new(*closed_prepared_order_response_actor, false), // 8 + AccountMeta::new(*closed_prepared_order_response_custody_token, false), // 9 + AccountMeta::new(*auction, false), // 10 + AccountMeta::new(*cctp_message, false), // 11 + AccountMeta::new(*cctp_mint, false), // 12 + AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), // 13 + AccountMeta::new(*cctp_message_transmitter_config, false), // 14 + AccountMeta::new_readonly(*cctp_token_messenger, false), // 15 + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), // 16 + AccountMeta::new(*cctp_token_minter, false), // 17 + AccountMeta::new(*cctp_local_token, false), // 18 + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), // 19 + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), // 20 + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), // 21 + AccountMeta::new_readonly(*core_bridge_program, false), // 22 + AccountMeta::new(*core_bridge_fee_collector, false), // 23 + AccountMeta::new(*core_bridge_config, false), // 24 + AccountMeta::new_readonly(*token_program, false), // 25 + AccountMeta::new_readonly(*system_program, false), // 26 + AccountMeta::new_readonly(*clock, false), // 27 + AccountMeta::new_readonly(*rent, false), // 28 + ], + data: FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(&self.data).to_vec(), + } } } pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) -> Result<()> { - let program_id = &crate::ID; require_min_account_infos_len(accounts, 29)?; let payer = &accounts[0]; - let post_shim_message = &accounts[1]; - let core_bridge_emitter_sequence = &accounts[2]; - let _post_message_shim_event_authority = &accounts[3]; - let _post_message_shim_program = &accounts[4]; - let cctp_message = &accounts[5]; - let custodian = &accounts[6]; - let fee_recipient_token = &accounts[7]; // Who is this? - let closed_prepared_order_response_actor = &accounts[8]; - let closed_prepared_order_response = &accounts[9]; - let closed_prepared_order_response_custody_token = &accounts[10]; - let auction = &accounts[11]; // Will be created here - let cctp_mint = &accounts[12]; - let cctp_token_messenger_minter_sender_authority = &accounts[13]; - let cctp_message_transmitter_config = &accounts[14]; - let cctp_token_messenger = &accounts[15]; - let cctp_remote_token_messenger = &accounts[16]; - let cctp_token_minter = &accounts[17]; - let cctp_local_token = &accounts[18]; - let cctp_token_messenger_minter_event_authority = &accounts[19]; - let cctp_token_messenger_minter_program = &accounts[20]; - let cctp_message_transmitter_program = &accounts[21]; - let _core_bridge_program = &accounts[22]; - let _core_bridge_fee_collector = &accounts[23]; - let _core_bridge_config = &accounts[24]; - let token_program = &accounts[25]; - let system_program = &accounts[26]; - let _clock = &accounts[27]; - let _rent = &accounts[28]; - - let mut prepared_order_response_account = PreparedOrderResponse::try_deserialize( - &mut &closed_prepared_order_response.data.borrow_mut()[..], - )?; - let fee_recipient_token_account = - &TokenAccount::try_deserialize(&mut &fee_recipient_token.data.borrow_mut()[..])?; - let prepared_custody_token_account = &TokenAccount::try_deserialize( - &mut &closed_prepared_order_response_custody_token - .data - .borrow_mut()[..], - )?; - - let to_router_endpoint = prepared_order_response_account.to_endpoint; - let destination_cctp_domain = match to_router_endpoint.protocol { - MessageProtocol::Cctp { domain } => domain, - _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()), - }; + let post_shim_infos = &accounts[1..=4]; - let auction_key = auction.key(); - - // Start of checks - // ------------------------------------------------------------------------------------------------ - - // Check cctp message is writable - if !cctp_message.is_writable { - msg!("Cctp message is not writable"); - return Err(MatchingEngineError::AccountNotWritable.into()) - .map_err(|e: Error| e.with_account_name("cctp_message")); - } + let custodian_info = &accounts[5]; + let checked_custodian = super::helpers::try_custodian_account(custodian_info, false)?; - // Check cctp message seeds are valid - let cctp_message_seeds = [ - common::CCTP_MESSAGE_SEED_PREFIX, - auction_key.as_ref(), - &[data.cctp_message_bump], - ]; - - let cctp_message_pda = Pubkey::create_program_address(&cctp_message_seeds, &ID) - .map_err(|_| MatchingEngineError::InvalidPda)?; - if cctp_message_pda != cctp_message.key() { - msg!("Cctp message seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()) - .map_err(|e: Error| e.with_pubkeys((cctp_message_pda, cctp_message.key()))); - }; - // Check custodian owner is the matching engine program and that it deserializes into a checked custodian - require_eq!(custodian.owner, &ID); - let checked_custodian = Custodian::try_deserialize(&mut &custodian.data.borrow_mut()[..])?; + let fee_recipient_token_info = &accounts[6]; // Check that the fee recipient token is the custodian's fee recipient token - require_eq!( - fee_recipient_token.key(), - checked_custodian.fee_recipient_token + require_keys_eq!( + *fee_recipient_token_info.key, + checked_custodian.fee_recipient_token, + MatchingEngineError::InvalidFeeRecipientToken ); - // Check seeds of prepared order response are valid + let closed_prepared_order_response_infos = &accounts[7..=9]; + + let closed_prepared_order_response_info = &closed_prepared_order_response_infos[0]; + let mut prepared_order_response = PreparedOrderResponse::try_deserialize( + &mut &closed_prepared_order_response_info.data.borrow_mut()[..], + )?; let prepared_order_response_pda = Pubkey::create_program_address( &[ PreparedOrderResponse::SEED_PREFIX, - prepared_order_response_account.seeds.fast_vaa_hash.as_ref(), - &[prepared_order_response_account.seeds.bump], + prepared_order_response.seeds.fast_vaa_hash.as_ref(), + &[prepared_order_response.seeds.bump], ], - program_id, + &ID, ) .map_err(|_| MatchingEngineError::InvalidPda)?; - if prepared_order_response_pda != closed_prepared_order_response.key() { - msg!("Prepared order response seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys(( - prepared_order_response_pda, - closed_prepared_order_response.key(), - )) - }); - }; + require_keys_eq!( + prepared_order_response_pda, + *closed_prepared_order_response_info.key, + MatchingEngineError::InvalidPda + ); + + let closed_prepared_order_response_actor_info = &closed_prepared_order_response_infos[1]; + // Check prepared by is the same as the prepared by in the accounts + require_keys_eq!( + prepared_order_response.prepared_by, + *closed_prepared_order_response_actor_info.key + ); + + let closed_prepared_order_response_custody_token_info = + &closed_prepared_order_response_infos[2]; + // Check seeds of prepared custody token are valid - { + let prepared_custody_token = { + // First do checks on the prepared custody token address let (prepared_custody_token_pda, _) = Pubkey::find_program_address( &[ crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, - closed_prepared_order_response.key().as_ref(), + closed_prepared_order_response_info.key.as_ref(), ], - program_id, + &ID, ); - if prepared_custody_token_pda != closed_prepared_order_response_custody_token.key() { - msg!("Prepared custody token seeds are invalid"); - return Err(ErrorCode::ConstraintSeeds.into()).map_err(|e: Error| { - e.with_pubkeys(( - prepared_custody_token_pda, - closed_prepared_order_response_custody_token.key(), - )) - }); - }; - } + require_keys_eq!( + prepared_custody_token_pda, + *closed_prepared_order_response_custody_token_info.key, + MatchingEngineError::InvalidPda + ); + Box::new(TokenAccount::try_deserialize( + &mut &closed_prepared_order_response_custody_token_info + .data + .borrow_mut()[..], + )?) + }; - // Check prepared by is the same as the prepared by in the accounts - require_eq!( - prepared_order_response_account.prepared_by, - closed_prepared_order_response_actor.key() - ); + let cctp_infos = &accounts[11..=21]; + + let _core_bridge_infos = &accounts[22..=24]; + let token_program = &accounts[25]; + let system_program = &accounts[26]; + let _required_sysvars = &accounts[27..=28]; - // Check that custody token is a token account - let _checked_prepared_custody_token = Box::new(TokenAccount::try_deserialize( - &mut &closed_prepared_order_response_custody_token - .data - .borrow_mut()[..], - )?); - // Check seeds of auction are valid + // Check seeds of prepared order response are valid + + // Begin of initialisation of auction account + // ------------------------------------------------------------------------------------------------ + let auction_info = &accounts[10]; // Will be created here + // Check seeds of auction are valid let auction_seeds = [ Auction::SEED_PREFIX, - prepared_order_response_account.seeds.fast_vaa_hash.as_ref(), + prepared_order_response.seeds.fast_vaa_hash.as_ref(), &[data.auction_bump], ]; - let auction_pda = Pubkey::create_program_address(&auction_seeds, program_id) + let auction_pda = Pubkey::create_program_address(&auction_seeds, &ID) .map_err(|_| MatchingEngineError::InvalidPda)?; - if auction_pda != auction.key() { - return Err(MatchingEngineError::InvalidPda.into()); - } - - // End of checks - // ------------------------------------------------------------------------------------------------ + require_keys_eq!( + auction_pda, + *auction_info.key, + MatchingEngineError::InvalidPda + ); - // Begin of initialisation of auction account - // ------------------------------------------------------------------------------------------------ let auction_space = 8 + Auction::INIT_SPACE_NO_AUCTION; let auction_signer_seeds = &[&auction_seeds[..]]; create_account_reliably( - &payer.key(), - &auction.key(), - auction.lamports(), + payer.key, + auction_info.key, + auction_info.lamports(), auction_space, accounts, - program_id, + &ID, auction_signer_seeds, )?; - // Borrow the account data mutably - let mut auction_data = auction - .try_borrow_mut_data() - .map_err(|_| MatchingEngineError::AccountNotWritable)?; - // Write the discriminator to the first 8 bytes - let discriminator = Auction::discriminator(); - auction_data[0..8].copy_from_slice(&discriminator); - let mut auction_to_write = - prepared_order_response_account.new_auction_placeholder(data.auction_bump); - let prepare_none_and_settle_fill_pubkeys = PrepareNoneAndSettleFillPubkeys { - prepared_order_response_key: &closed_prepared_order_response.key(), - prepared_order_response_custody_token: &closed_prepared_order_response_custody_token.key(), - fee_recipient_token_key: &fee_recipient_token.key(), - custodian_key: &custodian.key(), - }; + let mut auction = prepared_order_response.new_auction_placeholder(data.auction_bump); + let SettledNone { user_amount, fill, auction_settled_event: _, - } = prepare_none_and_settle_fill( - prepare_none_and_settle_fill_pubkeys, - &mut prepared_order_response_account, - &mut auction_to_write, - fee_recipient_token_account, - prepared_custody_token_account, - accounts, - )?; - let auction_bytes = auction_to_write - .try_to_vec() - .map_err(|_| MatchingEngineError::BorshDeserializationError)?; - auction_data[8..8_usize.saturating_add(auction_bytes.len())].copy_from_slice(&auction_bytes); + } = { + let fee_recipient_token = Box::new(TokenAccount::try_deserialize( + &mut &fee_recipient_token_info.data.borrow_mut()[..], + )?); + settle_none_and_prepare_fill( + SettleNoneAndPrepareFill { + prepared_order_response_key: closed_prepared_order_response_info.key, + prepared_order_response: &mut prepared_order_response, + prepared_custody_token_key: closed_prepared_order_response_custody_token_info.key, + prepared_custody_token: &prepared_custody_token, + auction: &mut auction, + fee_recipient_token_key: fee_recipient_token_info.key, + fee_recipient_token: &fee_recipient_token, + custodian_key: custodian_info.key, + }, + accounts, + )? + }; + + let new_auction: &mut [u8] = &mut accounts[10].try_borrow_mut_data()?; + let mut new_auction_cursor = std::io::Cursor::new(new_auction); + auction.try_serialize(&mut new_auction_cursor)?; + // ------------------------------------------------------------------------------------------------ // End of initialisation of auction account // Begin of burning and posting the message // ------------------------------------------------------------------------------------------------ + let cctp_message = &cctp_infos[0]; + + // Check cctp message is writable + if !cctp_message.is_writable { + msg!("Cctp message is not writable"); + return Err(MatchingEngineError::AccountNotWritable.into()) + .map_err(|e: Error| e.with_account_name("cctp_message")); + } + // Check cctp message seeds are valid + let cctp_message_seeds = [ + common::CCTP_MESSAGE_SEED_PREFIX, + auction_info.key.as_ref(), + &[data.cctp_message_bump], + ]; + + let cctp_message_pda = Pubkey::create_program_address(&cctp_message_seeds, &ID) + .map_err(|_| MatchingEngineError::InvalidPda)?; + require_keys_eq!( + cctp_message_pda, + *cctp_message.key, + MatchingEngineError::InvalidPda + ); + + let cctp_mint = &cctp_infos[1]; + let cctp_token_messenger_minter_sender_authority = &cctp_infos[2]; + let cctp_message_transmitter_config = &cctp_infos[3]; + let cctp_token_messenger = &cctp_infos[4]; + let cctp_remote_token_messenger = &cctp_infos[5]; + let cctp_token_minter = &cctp_infos[6]; + let cctp_local_token = &cctp_infos[7]; + let cctp_token_messenger_minter_event_authority = &cctp_infos[8]; + let cctp_token_messenger_minter_program = &cctp_infos[9]; + let cctp_message_transmitter_program = &cctp_infos[10]; + + let post_shim_message = &post_shim_infos[0]; + let core_bridge_emitter_sequence = &post_shim_infos[1]; + let _post_message_shim_event_authority = &post_shim_infos[2]; + let _post_message_shim_program = &post_shim_infos[3]; + let post_message_accounts = PostMessageAccounts { - emitter: custodian.key, + emitter: custodian_info.key, payer: payer.key, message: post_shim_message.key, sequence: core_bridge_emitter_sequence.key, }; + + // TODO: Do we need this check? The prepared order response will only be created with a correct endpoint? + let to_router_endpoint = prepared_order_response.to_endpoint; + let destination_cctp_domain = match to_router_endpoint.protocol { + MessageProtocol::Cctp { domain } => domain, + _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()), + }; burn_and_post( CpiContext::new_with_signer( cctp_token_messenger_minter_program.to_account_info(), common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { - burn_token_owner: custodian.to_account_info(), + burn_token_owner: custodian_info.to_account_info(), payer: payer.to_account_info(), token_messenger_minter_sender_authority: cctp_token_messenger_minter_sender_authority.to_account_info(), - burn_token: closed_prepared_order_response_custody_token.to_account_info(), + burn_token: closed_prepared_order_response_custody_token_info.to_account_info(), message_transmitter_config: cctp_message_transmitter_config.to_account_info(), token_messenger: cctp_token_messenger.to_account_info(), remote_token_messenger: cctp_remote_token_messenger.to_account_info(), @@ -355,7 +386,7 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - Custodian::SIGNER_SEEDS, &[ common::CCTP_MESSAGE_SEED_PREFIX, - auction.key().as_ref(), + auction_info.key.as_ref(), &[data.cctp_message_bump], ], ], @@ -379,9 +410,9 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - // ------------------------------------------------------------------------------------------------ let close_token_account_ix = spl_token::instruction::close_account( &spl_token::ID, - &closed_prepared_order_response_custody_token.key(), - &closed_prepared_order_response_actor.key(), - &custodian.key(), + closed_prepared_order_response_custody_token_info.key, + closed_prepared_order_response_actor_info.key, + custodian_info.key, &[], )?; invoke_signed_unchecked( @@ -394,100 +425,3 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - Ok(()) } - -struct PrepareNoneAndSettleFillPubkeys<'ix> { - prepared_order_response_key: &'ix Pubkey, - prepared_order_response_custody_token: &'ix Pubkey, - fee_recipient_token_key: &'ix Pubkey, - custodian_key: &'ix Pubkey, -} - -// Rewrite of settle_none_and_prepare_fill -fn prepare_none_and_settle_fill<'ix>( - pubkeys: PrepareNoneAndSettleFillPubkeys<'ix>, - prepared_order_response: &'ix mut PreparedOrderResponse, - auction: &mut Auction, - fee_recipient_token: &'ix TokenAccount, - prepared_custody_token: &'ix TokenAccount, - accounts: &[AccountInfo], -) -> Result { - let PrepareNoneAndSettleFillPubkeys { - prepared_order_response_key, - prepared_order_response_custody_token, - fee_recipient_token_key, - custodian_key, - } = pubkeys; - let prepared_order_response_signer_seeds = &[ - PreparedOrderResponse::SEED_PREFIX, - prepared_order_response.seeds.fast_vaa_hash.as_ref(), - &[prepared_order_response.seeds.bump], - ]; - // Pay the `fee_recipient` the base fee and init auction fee. This ensures that the protocol - // relayer is paid for relaying slow VAAs (which requires posting the fast order VAA) that do - // not have an associated auction. - let fee = prepared_order_response - .base_fee - .saturating_add(prepared_order_response.init_auction_fee); - - let transfer_ix = spl_token::instruction::transfer( - &spl_token::ID, - prepared_order_response_custody_token, - fee_recipient_token_key, - prepared_order_response_key, - &[], - fee, - )?; - - invoke_signed_unchecked( - &transfer_ix, - accounts, - &[prepared_order_response_signer_seeds], - )?; - - // Set authority instruction - let set_authority_ix = spl_token::instruction::set_authority( - &spl_token::ID, - prepared_order_response_custody_token, - Some(custodian_key), - spl_token::instruction::AuthorityType::AccountOwner, - prepared_order_response_key, - &[], - )?; - - invoke_signed_unchecked( - &set_authority_ix, - accounts, - &[prepared_order_response_signer_seeds], - )?; - - auction.status = AuctionStatus::Settled { - fee, - total_penalty: None, - }; - - let auction_settled_event = AuctionSettled { - fast_vaa_hash: auction.vaa_hash, - best_offer_token: Default::default(), - base_fee_token: crate::events::SettledTokenAccountInfo { - key: *fee_recipient_token_key, - balance_after: fee_recipient_token.amount.saturating_add(fee), - } - .into(), - with_execute: auction.target_protocol.into(), - }; - // TryInto is safe to unwrap here because the redeemer message had to have been able to fit in - // the prepared order response account (so it would not have exceed u32::MAX). - let redeemer_message = std::mem::take(&mut prepared_order_response.redeemer_message) - .try_into() - .unwrap(); - Ok(SettledNone { - user_amount: prepared_custody_token.amount.saturating_sub(fee), - fill: common::messages::Fill { - source_chain: prepared_order_response.source_chain, - order_sender: prepared_order_response.sender, - redeemer: prepared_order_response.redeemer, - redeemer_message, - }, - auction_settled_event, - }) -} diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs index b56db9c58..8eda9cf69 100644 --- a/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs +++ b/solana/programs/matching-engine/src/processor/auction/settle/none/cctp.rs @@ -100,18 +100,25 @@ fn handle_settle_auction_none_cctp( let custodian = &ctx.accounts.custodian; let token_program = &ctx.accounts.token_program; + let accounts_infos = ctx.accounts.to_account_infos(); + let super::SettledNone { user_amount: amount, fill, auction_settled_event, - } = super::settle_none_and_prepare_fill(super::SettleNoneAndPrepareFill { - prepared_order_response: &mut ctx.accounts.prepared.order_response, - prepared_custody_token, - auction: &mut ctx.accounts.auction, - fee_recipient_token: &ctx.accounts.fee_recipient_token, - custodian, - token_program, - })?; + } = super::settle_none_and_prepare_fill( + super::SettleNoneAndPrepareFill { + prepared_order_response_key: &ctx.accounts.prepared.order_response.key(), + prepared_order_response: &mut ctx.accounts.prepared.order_response, + prepared_custody_token_key: &ctx.accounts.prepared.custody_token.key(), + prepared_custody_token: &ctx.accounts.prepared.custody_token, + auction: &mut ctx.accounts.auction, + fee_recipient_token_key: &ctx.accounts.fee_recipient_token.key(), + fee_recipient_token: &ctx.accounts.fee_recipient_token, + custodian_key: &custodian.key(), + }, + &accounts_infos, + )?; let EndpointInfo { chain: _, diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs index 2cbede99d..eba75a465 100644 --- a/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs +++ b/solana/programs/matching-engine/src/processor/auction/settle/none/local.rs @@ -123,18 +123,25 @@ pub fn settle_auction_none_local(ctx: Context) -> Result let custodian = &ctx.accounts.custodian; let token_program = &ctx.accounts.token_program; + let accounts_infos = ctx.accounts.to_account_infos(); + let super::SettledNone { user_amount: amount, fill, auction_settled_event, - } = super::settle_none_and_prepare_fill(super::SettleNoneAndPrepareFill { - prepared_order_response: &mut ctx.accounts.prepared.order_response, - prepared_custody_token, - auction: &mut ctx.accounts.auction, - fee_recipient_token: &ctx.accounts.fee_recipient_token, - custodian, - token_program, - })?; + } = super::settle_none_and_prepare_fill( + super::SettleNoneAndPrepareFill { + prepared_order_response_key: &ctx.accounts.prepared.order_response.key(), + prepared_order_response: &mut ctx.accounts.prepared.order_response, + prepared_custody_token_key: &ctx.accounts.prepared.custody_token.key(), + prepared_custody_token: &ctx.accounts.prepared.custody_token, + auction: &mut ctx.accounts.auction, + fee_recipient_token_key: &ctx.accounts.fee_recipient_token.key(), + fee_recipient_token: &ctx.accounts.fee_recipient_token, + custodian_key: &custodian.key(), + }, + &accounts_infos, + )?; // Emit an event indicating that the auction has been settled. emit_cpi!(auction_settled_event); diff --git a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs index 0b1ac5076..2f3d67587 100644 --- a/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs +++ b/solana/programs/matching-engine/src/processor/auction/settle/none/mod.rs @@ -5,21 +5,23 @@ mod local; pub use local::*; use crate::{ - composite::*, events::AuctionSettled, state::{Auction, AuctionStatus, PreparedOrderResponse}, }; use anchor_lang::prelude::*; -use anchor_spl::token; +use anchor_spl::token::{spl_token, TokenAccount}; use common::messages::Fill; +use solana_program::program::invoke_signed_unchecked; -struct SettleNoneAndPrepareFill<'ctx, 'info> { - prepared_order_response: &'ctx mut Account<'info, PreparedOrderResponse>, - prepared_custody_token: &'ctx Account<'info, token::TokenAccount>, - auction: &'ctx mut Account<'info, Auction>, - fee_recipient_token: &'ctx Account<'info, token::TokenAccount>, - custodian: &'ctx CheckedCustodian<'info>, - token_program: &'ctx Program<'info, token::Token>, +pub struct SettleNoneAndPrepareFill<'ix> { + pub prepared_order_response_key: &'ix Pubkey, + pub prepared_order_response: &'ix mut PreparedOrderResponse, + pub prepared_custody_token_key: &'ix Pubkey, + pub prepared_custody_token: &'ix TokenAccount, + pub auction: &'ix mut Auction, + pub fee_recipient_token_key: &'ix Pubkey, + pub fee_recipient_token: &'ix TokenAccount, + pub custodian_key: &'ix Pubkey, } pub struct SettledNone { @@ -28,56 +30,63 @@ pub struct SettledNone { pub auction_settled_event: AuctionSettled, } -fn settle_none_and_prepare_fill(accounts: SettleNoneAndPrepareFill<'_, '_>) -> Result { +pub fn settle_none_and_prepare_fill( + accounts: SettleNoneAndPrepareFill<'_>, + accounts_infos: &[AccountInfo], +) -> Result { let SettleNoneAndPrepareFill { + prepared_order_response_key, prepared_order_response, + prepared_custody_token_key, prepared_custody_token, auction, + fee_recipient_token_key, fee_recipient_token, - custodian, - token_program, + custodian_key, } = accounts; - let prepared_order_response_signer_seeds = &[ PreparedOrderResponse::SEED_PREFIX, prepared_order_response.seeds.fast_vaa_hash.as_ref(), &[prepared_order_response.seeds.bump], ]; - // Pay the `fee_recipient` the base fee and init auction fee. This ensures that the protocol // relayer is paid for relaying slow VAAs (which requires posting the fast order VAA) that do // not have an associated auction. let fee = prepared_order_response .base_fee .saturating_add(prepared_order_response.init_auction_fee); - token::transfer( - CpiContext::new_with_signer( - token_program.to_account_info(), - token::Transfer { - from: prepared_custody_token.to_account_info(), - to: fee_recipient_token.to_account_info(), - authority: prepared_order_response.to_account_info(), - }, - &[prepared_order_response_signer_seeds], - ), + + let transfer_ix = spl_token::instruction::transfer( + &spl_token::ID, + prepared_custody_token_key, + fee_recipient_token_key, + prepared_order_response_key, + &[], fee, )?; - // Set the authority of the custody token account to the custodian. He will take over from here. - token::set_authority( - CpiContext::new_with_signer( - token_program.to_account_info(), - token::SetAuthority { - current_authority: prepared_order_response.to_account_info(), - account_or_mint: prepared_custody_token.to_account_info(), - }, - &[prepared_order_response_signer_seeds], - ), - token::spl_token::instruction::AuthorityType::AccountOwner, - custodian.key().into(), + invoke_signed_unchecked( + &transfer_ix, + accounts_infos, + &[prepared_order_response_signer_seeds], + )?; + + // Set authority instruction + let set_authority_ix = spl_token::instruction::set_authority( + &spl_token::ID, + prepared_custody_token_key, + Some(custodian_key), + spl_token::instruction::AuthorityType::AccountOwner, + prepared_order_response_key, + &[], + )?; + + invoke_signed_unchecked( + &set_authority_ix, + accounts_infos, + &[prepared_order_response_signer_seeds], )?; - // Indicate that the auction has been settled. auction.status = AuctionStatus::Settled { fee, total_penalty: None, @@ -87,13 +96,12 @@ fn settle_none_and_prepare_fill(accounts: SettleNoneAndPrepareFill<'_, '_>) -> R fast_vaa_hash: auction.vaa_hash, best_offer_token: Default::default(), base_fee_token: crate::events::SettledTokenAccountInfo { - key: fee_recipient_token.key(), + key: *fee_recipient_token_key, balance_after: fee_recipient_token.amount.saturating_add(fee), } .into(), with_execute: auction.target_protocol.into(), }; - // TryInto is safe to unwrap here because the redeemer message had to have been able to fit in // the prepared order response account (so it would not have exceed u32::MAX). let redeemer_message = std::mem::take(&mut prepared_order_response.redeemer_message) @@ -101,7 +109,7 @@ fn settle_none_and_prepare_fill(accounts: SettleNoneAndPrepareFill<'_, '_>) -> R .unwrap(); Ok(SettledNone { user_amount: prepared_custody_token.amount.saturating_sub(fee), - fill: Fill { + fill: common::messages::Fill { source_chain: prepared_order_response.source_chain, order_sender: prepared_order_response.sender, redeemer: prepared_order_response.redeemer, From 5c2301a525dd88cdccec06a7d2f7579991f36134 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 14 May 2025 19:15:50 +0100 Subject: [PATCH 109/112] working tests --- .../tests/shimful/shims_execute_order.rs | 82 +++++++++++++++++++ .../shimful/shims_prepare_order_response.rs | 1 + .../shimful/shims_settle_auction_none_cctp.rs | 2 +- .../tests/shimless/settle_auction.rs | 2 +- .../shimless/settle_auction_none_cctp.rs | 2 +- .../tests/utils/auction.rs | 5 +- .../processor/settle_auction_none_cctp.rs | 6 +- 7 files changed, 92 insertions(+), 8 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index fd49419a0..fb889b6ea 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -302,3 +302,85 @@ fn create_execute_order_shim_accounts<'ix>( clock: clock_id, // 31 } } + +pub struct CctpAccounts { + pub mint: Pubkey, + pub token_messenger: Pubkey, + pub token_messenger_minter_sender_authority: Pubkey, + pub token_messenger_minter_event_authority: Pubkey, + pub message_transmitter_config: Pubkey, + pub token_minter: Pubkey, + pub local_token: Pubkey, + pub remote_token_messenger: Pubkey, + pub token_messenger_minter_program: Pubkey, + pub message_transmitter_program: Pubkey, +} + +impl Into for CctpAccounts { + fn into(self) -> CctpDepositForBurn { + CctpDepositForBurn { + mint: self.mint, + local_token: self.local_token, + token_messenger_minter_sender_authority: self.token_messenger_minter_sender_authority, + message_transmitter_config: self.message_transmitter_config, + token_messenger: self.token_messenger, + remote_token_messenger: self.remote_token_messenger, + token_minter: self.token_minter, + token_messenger_minter_event_authority: self.token_messenger_minter_event_authority, + message_transmitter_program: self.message_transmitter_program, + token_messenger_minter_program: self.token_messenger_minter_program, + } + } +} + +pub fn create_cctp_accounts( + current_state: &TestingEngineState, + testing_context: &TestingContext, +) -> CctpAccounts { + let transfer_direction = current_state.base().transfer_direction; + let fixture_accounts = testing_context.get_fixture_accounts().unwrap(); + let remote_token_messenger = match transfer_direction { + TransferDirection::FromEthereumToArbitrum => { + fixture_accounts.arbitrum_remote_token_messenger + } + TransferDirection::FromArbitrumToEthereum => { + fixture_accounts.ethereum_remote_token_messenger + } + _ => panic!("Unsupported transfer direction"), + }; + let token_messenger_minter_sender_authority = + Pubkey::find_program_address(&[b"sender_authority"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let message_transmitter_config = + Pubkey::find_program_address(&[b"message_transmitter"], &MESSAGE_TRANSMITTER_PROGRAM_ID).0; + let token_messenger = + Pubkey::find_program_address(&[b"token_messenger"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let token_minter = + Pubkey::find_program_address(&[b"token_minter"], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + let local_token = Pubkey::find_program_address( + &[b"local_token", &USDC_MINT.to_bytes()], + &TOKEN_MESSENGER_MINTER_PROGRAM_ID, + ) + .0; + let token_messenger_minter_event_authority = + Pubkey::find_program_address(&[EVENT_AUTHORITY_SEED], &TOKEN_MESSENGER_MINTER_PROGRAM_ID).0; + CctpAccounts { + mint: utils::constants::USDC_MINT, + token_messenger, + token_messenger_minter_sender_authority, + token_messenger_minter_event_authority, + message_transmitter_config, + token_minter, + local_token, + remote_token_messenger, + token_messenger_minter_program: TOKEN_MESSENGER_MINTER_PROGRAM_ID, + message_transmitter_program: MESSAGE_TRANSMITTER_PROGRAM_ID, + } +} + +pub fn create_cctp_deposit_for_burn( + current_state: &TestingEngineState, + testing_context: &TestingContext, +) -> CctpDepositForBurn { + let cctp_accounts = create_cctp_accounts(current_state, testing_context); + cctp_accounts.into() +} diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs index 6a8cfac8f..a97fefb12 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_prepare_order_response.rs @@ -140,6 +140,7 @@ pub async fn prepare_order_response_cctp_shimful( prepared_custody_token: accounts.prepared_custody_token, base_fee_token: accounts.base_fee_token, actor_enum: config.actor_enum, + prepared_by: payer_signer.pubkey(), }; TestingEngineState::OrderPrepared { base: current_state.base().clone(), diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs index e9bb14cf7..ace5dddcf 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_settle_auction_none_cctp.rs @@ -61,7 +61,7 @@ pub async fn settle_auction_none_shimful( return current_state.auction_state().clone(); } - AuctionState::Settled + AuctionState::Settled(None) } struct SettleAuctionNoneCctpShimAccountsOwned { diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs index 162274621..1eb999834 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction.rs @@ -107,7 +107,7 @@ pub async fn settle_auction_complete( base: current_state.base().clone(), initialized: current_state.initialized().unwrap().clone(), router_endpoints: current_state.router_endpoints().unwrap().clone(), - auction_state: AuctionState::Settled(Box::new(active_auction.clone())), + auction_state: AuctionState::Settled(Some(Box::new(active_auction.clone()))), fast_market_order: current_state.fast_market_order().cloned(), order_prepared: current_state.order_prepared().unwrap().clone(), auction_accounts: current_state.auction_accounts().cloned(), diff --git a/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs b/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs index 6a45dd56c..f3ab49c13 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/settle_auction_none_cctp.rs @@ -63,7 +63,7 @@ pub async fn settle_auction_none_shimless( return current_state.auction_state().clone(); } - AuctionState::Settled + AuctionState::Settled(None) } async fn create_settle_auction_none_cctp_shimless_accounts( diff --git a/solana/modules/matching-engine-testing/tests/utils/auction.rs b/solana/modules/matching-engine-testing/tests/utils/auction.rs index fc8cf4fd1..332ad7f1c 100644 --- a/solana/modules/matching-engine-testing/tests/utils/auction.rs +++ b/solana/modules/matching-engine-testing/tests/utils/auction.rs @@ -47,7 +47,7 @@ pub struct AuctionAccounts { pub enum AuctionState { Active(Box), Paused(Box), - Settled(Box), + Settled(Option>), Inactive, } @@ -57,7 +57,8 @@ impl AuctionState { AuctionState::Active(auction) => Some(auction), AuctionState::Paused(auction) => Some(auction), AuctionState::Inactive => None, - AuctionState::Settled(auction) => Some(auction), + AuctionState::Settled(Some(auction)) => Some(auction), + AuctionState::Settled(None) => None, } } diff --git a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs index 113233d6b..6c5f981c4 100644 --- a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs +++ b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs @@ -185,9 +185,9 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - let closed_prepared_order_response_infos = &accounts[7..=9]; let closed_prepared_order_response_info = &closed_prepared_order_response_infos[0]; - let mut prepared_order_response = PreparedOrderResponse::try_deserialize( + let mut prepared_order_response = Box::new(PreparedOrderResponse::try_deserialize( &mut &closed_prepared_order_response_info.data.borrow_mut()[..], - )?; + )?); let prepared_order_response_pda = Pubkey::create_program_address( &[ PreparedOrderResponse::SEED_PREFIX, @@ -273,7 +273,7 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - &ID, auction_signer_seeds, )?; - let mut auction = prepared_order_response.new_auction_placeholder(data.auction_bump); + let mut auction = Box::new(prepared_order_response.new_auction_placeholder(data.auction_bump)); let SettledNone { user_amount, From 09ce66a39bb363690afe27fa8b1fa6e1473079b1 Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Wed, 14 May 2025 19:34:50 +0100 Subject: [PATCH 110/112] make lint --- .../tests/shimful/fast_market_order_shim.rs | 2 +- .../tests/shimful/shims_execute_order.rs | 28 ++++++++++--------- .../tests/shimful/shims_make_offer.rs | 2 +- .../tests/shimless/initialize.rs | 3 +- .../tests/shimless/make_offer.rs | 4 +-- .../tests/testing_engine/config.rs | 14 +--------- .../src/fallback/processor/burn_and_post.rs | 4 +-- .../src/fallback/processor/helpers.rs | 8 ++++-- .../processor/prepare_order_response.rs | 2 +- .../fallback/processor/process_instruction.rs | 9 ++++-- 10 files changed, 36 insertions(+), 40 deletions(-) diff --git a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs index 849539942..44932106b 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/fast_market_order_shim.rs @@ -62,7 +62,7 @@ pub async fn initialize_fast_market_order_shimful( .clone() .unwrap_or_else(|| testing_context.testing_actors.payer_signer.clone()); let guardian_signature_info = create_guardian_signatures( - &testing_context, + testing_context, test_context, &payer_signer, &fast_transfer_vaa.vaa_data, diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs index fb889b6ea..9fb3c6d49 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_execute_order.rs @@ -316,19 +316,21 @@ pub struct CctpAccounts { pub message_transmitter_program: Pubkey, } -impl Into for CctpAccounts { - fn into(self) -> CctpDepositForBurn { - CctpDepositForBurn { - mint: self.mint, - local_token: self.local_token, - token_messenger_minter_sender_authority: self.token_messenger_minter_sender_authority, - message_transmitter_config: self.message_transmitter_config, - token_messenger: self.token_messenger, - remote_token_messenger: self.remote_token_messenger, - token_minter: self.token_minter, - token_messenger_minter_event_authority: self.token_messenger_minter_event_authority, - message_transmitter_program: self.message_transmitter_program, - token_messenger_minter_program: self.token_messenger_minter_program, +impl From for CctpDepositForBurn { + fn from(cctp_accounts: CctpAccounts) -> Self { + Self { + mint: cctp_accounts.mint, + local_token: cctp_accounts.local_token, + token_messenger_minter_sender_authority: cctp_accounts + .token_messenger_minter_sender_authority, + message_transmitter_config: cctp_accounts.message_transmitter_config, + token_messenger: cctp_accounts.token_messenger, + remote_token_messenger: cctp_accounts.remote_token_messenger, + token_minter: cctp_accounts.token_minter, + token_messenger_minter_event_authority: cctp_accounts + .token_messenger_minter_event_authority, + message_transmitter_program: cctp_accounts.message_transmitter_program, + token_messenger_minter_program: cctp_accounts.token_messenger_minter_program, } } } diff --git a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs index 51a525b74..8f830fb8c 100644 --- a/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimful/shims_make_offer.rs @@ -164,7 +164,7 @@ pub async fn evaluate_place_initial_offer_shimful_state( .get_active_auction() .unwrap(); active_auction_state - .verify_auction(&testing_context, test_context) + .verify_auction(testing_context, test_context) .await .expect("Could not verify auction"); let auction_accounts = initial_offer_placed_state.auction_accounts; diff --git a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs index 52a510a47..24234dd3a 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/initialize.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/initialize.rs @@ -143,8 +143,7 @@ pub fn initialize_program_instruction( ) -> Instruction { let program_id = testing_context.get_matching_engine_program_id(); let usdc_mint_address = testing_context.get_usdc_mint_address(); - let initialize_addresses = - InitializeAddresses::new(testing_context, &auction_parameters_config); + let initialize_addresses = InitializeAddresses::new(testing_context, auction_parameters_config); let InitializeAddresses { custodian_address: custodian, auction_config_address: auction_config, diff --git a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs index 26cd89f6b..ce7823668 100644 --- a/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs +++ b/solana/modules/matching-engine-testing/tests/shimless/make_offer.rs @@ -275,7 +275,7 @@ pub async fn place_initial_offer_shimless( auction_state .get_active_auction() .unwrap() - .verify_auction(&testing_context, test_context) + .verify_auction(testing_context, test_context) .await .expect("Could not verify auction state"); return TestingEngineState::InitialOfferPlaced { @@ -413,7 +413,7 @@ pub async fn improve_offer( new_auction_state .get_active_auction() .unwrap() - .verify_auction(&testing_context, test_context) + .verify_auction(testing_context, test_context) .await .expect("Could not verify auction state"); return TestingEngineState::OfferImproved { diff --git a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs index 7c55f8bc7..f2a7c63d1 100644 --- a/solana/modules/matching-engine-testing/tests/testing_engine/config.rs +++ b/solana/modules/matching-engine-testing/tests/testing_engine/config.rs @@ -639,6 +639,7 @@ pub struct BalanceChange { pub usdt: i32, } +#[derive(Default)] pub struct CombinedInstructionConfig { pub create_fast_market_order_config: Option, pub place_initial_offer_config: Option, @@ -648,19 +649,6 @@ pub struct CombinedInstructionConfig { pub improve_offer_config: Option, } -impl Default for CombinedInstructionConfig { - fn default() -> Self { - Self { - create_fast_market_order_config: None, - place_initial_offer_config: None, - execute_order_config: None, - settle_auction_config: None, - close_fast_market_order_config: None, - improve_offer_config: None, - } - } -} - impl CombinedInstructionConfig { pub fn create_fast_market_order_and_place_initial_offer( testing_actors: &TestingActors, diff --git a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs index eda9a5884..3230cfbaa 100644 --- a/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs +++ b/solana/programs/matching-engine/src/fallback/processor/burn_and_post.rs @@ -53,8 +53,8 @@ pub fn burn_and_post<'info>( payer, wormhole_program_id: &CORE_BRIDGE_PROGRAM_ID, derived: post_message::PostMessageDerivedAccounts { - message: Some(&message), - sequence: Some(&sequence), + message: Some(message), + sequence: Some(sequence), core_bridge_config: Some(&CORE_BRIDGE_CONFIG), fee_collector: Some(&CORE_BRIDGE_FEE_COLLECTOR), event_authority: Some(&POST_MESSAGE_SHIM_EVENT_AUTHORITY), diff --git a/solana/programs/matching-engine/src/fallback/processor/helpers.rs b/solana/programs/matching-engine/src/fallback/processor/helpers.rs index 28b0528e1..a6ab86739 100644 --- a/solana/programs/matching-engine/src/fallback/processor/helpers.rs +++ b/solana/programs/matching-engine/src/fallback/processor/helpers.rs @@ -129,7 +129,7 @@ pub fn try_fast_market_order_account<'a>( return Err(ErrorCode::AccountDiscriminatorNotFound.into()); } - if &data[0..8] != &FastMarketOrder::DISCRIMINATOR { + if data[0..8] != FastMarketOrder::DISCRIMINATOR { return Err(ErrorCode::AccountDiscriminatorMismatch.into()); } @@ -137,7 +137,11 @@ pub fn try_fast_market_order_account<'a>( super::helpers::require_owned_by_this_program(fast_market_order_info, "fast_market_order")?; Ok(Ref::map(data, |data| { - bytemuck::from_bytes(&data[8..8 + std::mem::size_of::()]) + bytemuck::from_bytes( + &data[8..8_usize + .checked_add(std::mem::size_of::()) + .unwrap()], + ) })) } diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index 27f7e39e9..de948a290 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -393,7 +393,7 @@ pub fn prepare_order_response_cctp_shim( &spl_token::ID, &CCTP_MINT_RECIPIENT, &expected_prepared_custody_key, - &custodian_info.key, + custodian_info.key, &[], // Apparently this is only for multi-sig accounts amount_in, ) diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index ee6124964..f48181b17 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -114,7 +114,8 @@ impl FallbackMatchingEngineInstruction<'_> { match self { Self::InitializeFastMarketOrder(data) => { let mut out = Vec::with_capacity( - SELECTOR_SIZE + std::mem::size_of::(), + SELECTOR_SIZE + .saturating_add(std::mem::size_of::()), ); out.extend_from_slice( @@ -125,7 +126,8 @@ impl FallbackMatchingEngineInstruction<'_> { out } Self::PlaceInitialOfferCctpShim(data) => { - let mut out = Vec::with_capacity(SELECTOR_SIZE + std::mem::size_of::()); + let mut out = + Vec::with_capacity(SELECTOR_SIZE.saturating_add(std::mem::size_of::())); out.extend_from_slice( &FallbackMatchingEngineInstruction::PLACE_INITIAL_OFFER_CCTP_SHIM_SELECTOR, @@ -156,7 +158,8 @@ impl FallbackMatchingEngineInstruction<'_> { } FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(data) => { let mut out = Vec::with_capacity( - SELECTOR_SIZE + std::mem::size_of::(), + SELECTOR_SIZE + .saturating_add(std::mem::size_of::()), ); out.extend_from_slice( From 0c4dd7026868b4026957fd4fc24013a973a10b5b Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 15 May 2025 09:36:54 -0500 Subject: [PATCH 111/112] solana: clean up settle auction none --- .../processor/close_fast_market_order.rs | 3 +- .../src/fallback/processor/execute_order.rs | 1 + .../processor/initialize_fast_market_order.rs | 1 + .../fallback/processor/place_initial_offer.rs | 6 +- .../processor/prepare_order_response.rs | 3 +- .../fallback/processor/process_instruction.rs | 31 +- .../processor/settle_auction_none_cctp.rs | 311 ++++++++---------- 7 files changed, 161 insertions(+), 195 deletions(-) diff --git a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs index 7524d8b85..3c3df9773 100644 --- a/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/close_fast_market_order.rs @@ -42,7 +42,8 @@ impl CloseFastMarketOrder<'_> { } } -pub fn process(accounts: &[AccountInfo]) -> Result<()> { +#[inline(never)] +pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; // We need to check the refund recipient account against what we know as the diff --git a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs index 71345d6e5..5d6d291a7 100644 --- a/solana/programs/matching-engine/src/fallback/processor/execute_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/execute_order.rs @@ -188,6 +188,7 @@ impl ExecuteOrderCctpShim<'_> { } } +#[inline(never)] pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { // This saves stack space whereas having that in the body does not super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; diff --git a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs index 84ee1e6bc..ee1b7c2cb 100644 --- a/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs +++ b/solana/programs/matching-engine/src/fallback/processor/initialize_fast_market_order.rs @@ -95,6 +95,7 @@ impl InitializeFastMarketOrder<'_> { } } +#[inline(never)] pub(super) fn process( accounts: &[AccountInfo], data: &InitializeFastMarketOrderData, diff --git a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs index e92d1522e..715fa79c2 100644 --- a/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs +++ b/solana/programs/matching-engine/src/fallback/processor/place_initial_offer.rs @@ -112,7 +112,11 @@ impl PlaceInitialOfferCctpShim<'_> { } } -pub fn process(accounts: &[AccountInfo], data: &PlaceInitialOfferCctpShimData) -> Result<()> { +#[inline(never)] +pub(super) fn process( + accounts: &[AccountInfo], + data: &PlaceInitialOfferCctpShimData, +) -> Result<()> { // Check all accounts are valid super::helpers::require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; diff --git a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs index de948a290..9c49dd8fc 100644 --- a/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs +++ b/solana/programs/matching-engine/src/fallback/processor/prepare_order_response.rs @@ -154,7 +154,8 @@ impl<'ix> PrepareOrderResponseCctpShim<'ix> { } } -pub fn prepare_order_response_cctp_shim( +#[inline(never)] +pub(super) fn process( accounts: &[AccountInfo], data: PrepareOrderResponseCctpShimData, ) -> Result<()> { diff --git a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs index f48181b17..8287b1b2d 100644 --- a/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs +++ b/solana/programs/matching-engine/src/fallback/processor/process_instruction.rs @@ -5,10 +5,7 @@ use crate::ID; use super::initialize_fast_market_order::InitializeFastMarketOrderData; use super::place_initial_offer::PlaceInitialOfferCctpShimData; -use super::prepare_order_response::{ - prepare_order_response_cctp_shim, PrepareOrderResponseCctpShimData, -}; -use super::settle_auction_none_cctp::SettleAuctionNoneCctpShimData; +use super::prepare_order_response::PrepareOrderResponseCctpShimData; const SELECTOR_SIZE: usize = 8; @@ -34,7 +31,7 @@ pub enum FallbackMatchingEngineInstruction<'ix> { PlaceInitialOfferCctpShim(&'ix PlaceInitialOfferCctpShimData), ExecuteOrderCctpShim, PrepareOrderResponseCctpShim(PrepareOrderResponseCctpShimData), - SettleAuctionNoneCctpShim(&'ix SettleAuctionNoneCctpShimData), + SettleAuctionNoneCctpShim, } pub fn process_instruction( @@ -63,10 +60,10 @@ pub fn process_instruction( super::execute_order::process(accounts) } FallbackMatchingEngineInstruction::PrepareOrderResponseCctpShim(data) => { - prepare_order_response_cctp_shim(accounts, data) + super::prepare_order_response::process(accounts, data) } - FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(data) => { - super::settle_auction_none_cctp::process(accounts, data) + FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim => { + super::settle_auction_none_cctp::process(accounts) } } } @@ -100,9 +97,7 @@ impl<'ix> FallbackMatchingEngineInstruction<'ix> { .map(Self::PrepareOrderResponseCctpShim) } FallbackMatchingEngineInstruction::SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR => { - bytemuck::try_from_bytes(&instruction_data[SELECTOR_SIZE..]) - .ok() - .map(Self::SettleAuctionNoneCctpShim) + Some(Self::SettleAuctionNoneCctpShim) } _ => None, } @@ -156,18 +151,8 @@ impl FallbackMatchingEngineInstruction<'_> { out } - FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(data) => { - let mut out = Vec::with_capacity( - SELECTOR_SIZE - .saturating_add(std::mem::size_of::()), - ); - - out.extend_from_slice( - &FallbackMatchingEngineInstruction::SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR, - ); - out.extend_from_slice(bytemuck::bytes_of(*data)); - - out + FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim => { + FallbackMatchingEngineInstruction::SETTLE_AUCTION_NONE_CCTP_SHIM_SELECTOR.to_vec() } } } diff --git a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs index 6c5f981c4..25e0e2571 100644 --- a/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs +++ b/solana/programs/matching-engine/src/fallback/processor/settle_auction_none_cctp.rs @@ -1,18 +1,15 @@ -use crate::{ - processor::{settle_none_and_prepare_fill, SettleNoneAndPrepareFill}, - ID, -}; use anchor_lang::prelude::*; use anchor_spl::token::{spl_token, TokenAccount}; use bytemuck::{Pod, Zeroable}; use common::wormhole_io::TypePrefixedPayload; -use solana_program::instruction::Instruction; -use solana_program::program::invoke_signed_unchecked; +use solana_program::{instruction::Instruction, program::invoke_signed_unchecked}; use crate::{ error::MatchingEngineError, processor::SettledNone, + processor::{settle_none_and_prepare_fill, SettleNoneAndPrepareFill}, state::{Auction, Custodian, MessageProtocol, PreparedOrderResponse}, + ID, }; use super::{ @@ -21,6 +18,9 @@ use super::{ FallbackMatchingEngineInstruction, }; +const NUM_ACCOUNTS: usize = 28; + +// TODO: Remove #[derive(Debug, Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct SettleAuctionNoneCctpShimData { @@ -80,18 +80,23 @@ pub struct SettleAuctionNoneCctpShimAccounts<'ix> { /// Core bridge config pub core_bridge_config: &'ix Pubkey, // 24 /// Token program + // TODO: Remove pub token_program: &'ix Pubkey, // 25 /// System program + // TODO: Remove pub system_program: &'ix Pubkey, // 26 /// Clock + // TODO: Remove pub clock: &'ix Pubkey, // 27 /// Rent + // TODO: Remove pub rent: &'ix Pubkey, // 28 } pub struct SettleAuctionNoneCctpShim<'ix> { pub program_id: &'ix Pubkey, pub accounts: SettleAuctionNoneCctpShimAccounts<'ix>, + // TODO: Remove pub data: SettleAuctionNoneCctpShimData, } @@ -123,157 +128,137 @@ impl<'ix> SettleAuctionNoneCctpShim<'ix> { core_bridge_program, core_bridge_fee_collector, core_bridge_config, - token_program, - system_program, - clock, - rent, + token_program: _, + system_program: _, + clock: _, + rent: _, } = self.accounts; + + let accounts = vec![ + AccountMeta::new_readonly(*payer, true), // 0 + AccountMeta::new(*post_shim_message, false), // 1 + AccountMeta::new(*core_bridge_emitter_sequence, false), // 2 + AccountMeta::new_readonly(*post_message_shim_event_authority, false), // 3 + AccountMeta::new_readonly(*post_message_shim_program, false), // 4 + AccountMeta::new(*custodian, false), // 5 + AccountMeta::new(*fee_recipient_token, false), // 6 + AccountMeta::new(*closed_prepared_order_response, false), // 7 + AccountMeta::new(*closed_prepared_order_response_actor, false), // 8 + AccountMeta::new(*closed_prepared_order_response_custody_token, false), // 9 + AccountMeta::new(*auction, false), // 10 + AccountMeta::new(*cctp_message, false), // 11 + AccountMeta::new(*cctp_mint, false), // 12 + AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), // 13 + AccountMeta::new(*cctp_message_transmitter_config, false), // 14 + AccountMeta::new_readonly(*cctp_token_messenger, false), // 15 + AccountMeta::new_readonly(*cctp_remote_token_messenger, false), // 16 + AccountMeta::new(*cctp_token_minter, false), // 17 + AccountMeta::new(*cctp_local_token, false), // 18 + AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), // 19 + AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), // 20 + AccountMeta::new_readonly(*cctp_message_transmitter_program, false), // 21 + AccountMeta::new_readonly(*core_bridge_program, false), // 22 + AccountMeta::new(*core_bridge_fee_collector, false), // 23 + AccountMeta::new(*core_bridge_config, false), // 24 + AccountMeta::new_readonly(spl_token::ID, false), // 25 + AccountMeta::new_readonly(solana_program::system_program::ID, false), // 26 + AccountMeta::new_readonly(solana_program::sysvar::clock::ID, false), // 27 + ]; + debug_assert_eq!(accounts.len(), NUM_ACCOUNTS); + Instruction { program_id: *self.program_id, - accounts: vec![ - AccountMeta::new_readonly(*payer, true), // 0 - AccountMeta::new(*post_shim_message, false), // 1 - AccountMeta::new(*core_bridge_emitter_sequence, false), // 2 - AccountMeta::new_readonly(*post_message_shim_event_authority, false), // 3 - AccountMeta::new_readonly(*post_message_shim_program, false), // 4 - AccountMeta::new(*custodian, false), // 5 - AccountMeta::new(*fee_recipient_token, false), // 6 - AccountMeta::new_readonly(*closed_prepared_order_response, false), // 7 - AccountMeta::new(*closed_prepared_order_response_actor, false), // 8 - AccountMeta::new(*closed_prepared_order_response_custody_token, false), // 9 - AccountMeta::new(*auction, false), // 10 - AccountMeta::new(*cctp_message, false), // 11 - AccountMeta::new(*cctp_mint, false), // 12 - AccountMeta::new_readonly(*cctp_token_messenger_minter_sender_authority, false), // 13 - AccountMeta::new(*cctp_message_transmitter_config, false), // 14 - AccountMeta::new_readonly(*cctp_token_messenger, false), // 15 - AccountMeta::new_readonly(*cctp_remote_token_messenger, false), // 16 - AccountMeta::new(*cctp_token_minter, false), // 17 - AccountMeta::new(*cctp_local_token, false), // 18 - AccountMeta::new_readonly(*cctp_token_messenger_minter_event_authority, false), // 19 - AccountMeta::new_readonly(*cctp_token_messenger_minter_program, false), // 20 - AccountMeta::new_readonly(*cctp_message_transmitter_program, false), // 21 - AccountMeta::new_readonly(*core_bridge_program, false), // 22 - AccountMeta::new(*core_bridge_fee_collector, false), // 23 - AccountMeta::new(*core_bridge_config, false), // 24 - AccountMeta::new_readonly(*token_program, false), // 25 - AccountMeta::new_readonly(*system_program, false), // 26 - AccountMeta::new_readonly(*clock, false), // 27 - AccountMeta::new_readonly(*rent, false), // 28 - ], - data: FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim(&self.data).to_vec(), + accounts, + data: FallbackMatchingEngineInstruction::SettleAuctionNoneCctpShim.to_vec(), } } } -pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) -> Result<()> { - require_min_account_infos_len(accounts, 29)?; - let payer = &accounts[0]; - let post_shim_infos = &accounts[1..=4]; +#[inline(never)] +pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> { + require_min_account_infos_len(accounts, NUM_ACCOUNTS)?; + + let payer_info = &accounts[0]; + let post_shim_infos = &accounts[1..5]; let custodian_info = &accounts[5]; - let checked_custodian = super::helpers::try_custodian_account(custodian_info, false)?; + let custodian = super::helpers::try_custodian_account(custodian_info, false)?; let fee_recipient_token_info = &accounts[6]; + // Check that the fee recipient token is the custodian's fee recipient token require_keys_eq!( *fee_recipient_token_info.key, - checked_custodian.fee_recipient_token, + custodian.fee_recipient_token, MatchingEngineError::InvalidFeeRecipientToken ); - let closed_prepared_order_response_infos = &accounts[7..=9]; - - let closed_prepared_order_response_info = &closed_prepared_order_response_infos[0]; - let mut prepared_order_response = Box::new(PreparedOrderResponse::try_deserialize( - &mut &closed_prepared_order_response_info.data.borrow_mut()[..], - )?); - let prepared_order_response_pda = Pubkey::create_program_address( - &[ - PreparedOrderResponse::SEED_PREFIX, - prepared_order_response.seeds.fast_vaa_hash.as_ref(), - &[prepared_order_response.seeds.bump], - ], - &ID, + let prepared_order_response_info = &accounts[7]; + super::helpers::require_owned_by_this_program( + prepared_order_response_info, + "prepared_order_response", + )?; + let mut prepared_order_response = PreparedOrderResponse::try_deserialize( + &mut &prepared_order_response_info.data.borrow()[..], ) - .map_err(|_| MatchingEngineError::InvalidPda)?; - require_keys_eq!( - prepared_order_response_pda, - *closed_prepared_order_response_info.key, - MatchingEngineError::InvalidPda - ); + .map(Box::new)?; + + let original_preparer_info = &accounts[8]; - let closed_prepared_order_response_actor_info = &closed_prepared_order_response_infos[1]; // Check prepared by is the same as the prepared by in the accounts require_keys_eq!( + *original_preparer_info.key, prepared_order_response.prepared_by, - *closed_prepared_order_response_actor_info.key + MatchingEngineError::PreparedByMismatch, ); - let closed_prepared_order_response_custody_token_info = - &closed_prepared_order_response_infos[2]; + let prepared_custody_info = &accounts[9]; - // Check seeds of prepared custody token are valid - let prepared_custody_token = { - // First do checks on the prepared custody token address - let (prepared_custody_token_pda, _) = Pubkey::find_program_address( - &[ - crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, - closed_prepared_order_response_info.key.as_ref(), - ], - &ID, - ); - require_keys_eq!( - prepared_custody_token_pda, - *closed_prepared_order_response_custody_token_info.key, - MatchingEngineError::InvalidPda - ); - Box::new(TokenAccount::try_deserialize( - &mut &closed_prepared_order_response_custody_token_info - .data - .borrow_mut()[..], - )?) - }; + // First do checks on the prepared custody token address + let (expected_prepared_custody_key, _) = Pubkey::find_program_address( + &[ + crate::PREPARED_CUSTODY_TOKEN_SEED_PREFIX, + prepared_order_response_info.key.as_ref(), + ], + &ID, + ); + + let prepared_custody = + TokenAccount::try_deserialize(&mut &prepared_custody_info.data.borrow()[..]) + .map(Box::new)?; let cctp_infos = &accounts[11..=21]; let _core_bridge_infos = &accounts[22..=24]; let token_program = &accounts[25]; let system_program = &accounts[26]; - let _required_sysvars = &accounts[27..=28]; - - // Check seeds of prepared order response are valid - - // Begin of initialisation of auction account - // ------------------------------------------------------------------------------------------------ - let auction_info = &accounts[10]; // Will be created here - // Check seeds of auction are valid - let auction_seeds = [ - Auction::SEED_PREFIX, - prepared_order_response.seeds.fast_vaa_hash.as_ref(), - &[data.auction_bump], - ]; - let auction_pda = Pubkey::create_program_address(&auction_seeds, &ID) - .map_err(|_| MatchingEngineError::InvalidPda)?; - require_keys_eq!( - auction_pda, - *auction_info.key, - MatchingEngineError::InvalidPda - ); - let auction_space = 8 + Auction::INIT_SPACE_NO_AUCTION; + let auction_placeholder_info = &accounts[10]; + + let (expected_auction_placeholder_key, auction_placeholder_bump) = Pubkey::find_program_address( + &[ + Auction::SEED_PREFIX, + &prepared_order_response.seeds.fast_vaa_hash, + ], + &ID, + ); - let auction_signer_seeds = &[&auction_seeds[..]]; create_account_reliably( - payer.key, - auction_info.key, - auction_info.lamports(), - auction_space, + payer_info.key, + &expected_auction_placeholder_key, + auction_placeholder_info.lamports(), + 8 + Auction::INIT_SPACE_NO_AUCTION, accounts, &ID, - auction_signer_seeds, + &[&[ + Auction::SEED_PREFIX, + &prepared_order_response.seeds.fast_vaa_hash, + &[auction_placeholder_bump], + ]], )?; - let mut auction = Box::new(prepared_order_response.new_auction_placeholder(data.auction_bump)); + + let mut auction = + Box::new(prepared_order_response.new_auction_placeholder(auction_placeholder_bump)); let SettledNone { user_amount, @@ -285,10 +270,10 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - )?); settle_none_and_prepare_fill( SettleNoneAndPrepareFill { - prepared_order_response_key: closed_prepared_order_response_info.key, + prepared_order_response_key: prepared_order_response_info.key, prepared_order_response: &mut prepared_order_response, - prepared_custody_token_key: closed_prepared_order_response_custody_token_info.key, - prepared_custody_token: &prepared_custody_token, + prepared_custody_token_key: prepared_custody_info.key, + prepared_custody_token: &prepared_custody, auction: &mut auction, fee_recipient_token_key: fee_recipient_token_info.key, fee_recipient_token: &fee_recipient_token, @@ -298,36 +283,20 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - )? }; - let new_auction: &mut [u8] = &mut accounts[10].try_borrow_mut_data()?; + let new_auction: &mut [u8] = &mut auction_placeholder_info.try_borrow_mut_data()?; let mut new_auction_cursor = std::io::Cursor::new(new_auction); auction.try_serialize(&mut new_auction_cursor)?; - // ------------------------------------------------------------------------------------------------ - // End of initialisation of auction account - - // Begin of burning and posting the message - // ------------------------------------------------------------------------------------------------ + // Prepare to invoke CCTP deposit for burn along with posting Wormhole + // message. let cctp_message = &cctp_infos[0]; - // Check cctp message is writable - if !cctp_message.is_writable { - msg!("Cctp message is not writable"); - return Err(MatchingEngineError::AccountNotWritable.into()) - .map_err(|e: Error| e.with_account_name("cctp_message")); - } - // Check cctp message seeds are valid - let cctp_message_seeds = [ - common::CCTP_MESSAGE_SEED_PREFIX, - auction_info.key.as_ref(), - &[data.cctp_message_bump], - ]; - - let cctp_message_pda = Pubkey::create_program_address(&cctp_message_seeds, &ID) - .map_err(|_| MatchingEngineError::InvalidPda)?; - require_keys_eq!( - cctp_message_pda, - *cctp_message.key, - MatchingEngineError::InvalidPda + let (_, new_cctp_message_bump) = Pubkey::find_program_address( + &[ + common::CCTP_MESSAGE_SEED_PREFIX, + auction_placeholder_info.key.as_ref(), + ], + &ID, ); let cctp_mint = &cctp_infos[1]; @@ -346,28 +315,21 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - let _post_message_shim_event_authority = &post_shim_infos[2]; let _post_message_shim_program = &post_shim_infos[3]; - let post_message_accounts = PostMessageAccounts { - emitter: custodian_info.key, - payer: payer.key, - message: post_shim_message.key, - sequence: core_bridge_emitter_sequence.key, - }; - - // TODO: Do we need this check? The prepared order response will only be created with a correct endpoint? let to_router_endpoint = prepared_order_response.to_endpoint; let destination_cctp_domain = match to_router_endpoint.protocol { MessageProtocol::Cctp { domain } => domain, _ => return Err(MatchingEngineError::InvalidCctpEndpoint.into()), }; + burn_and_post( CpiContext::new_with_signer( cctp_token_messenger_minter_program.to_account_info(), common::wormhole_cctp_solana::cpi::DepositForBurnWithCaller { burn_token_owner: custodian_info.to_account_info(), - payer: payer.to_account_info(), + payer: payer_info.to_account_info(), token_messenger_minter_sender_authority: cctp_token_messenger_minter_sender_authority.to_account_info(), - burn_token: closed_prepared_order_response_custody_token_info.to_account_info(), + burn_token: prepared_custody_info.to_account_info(), message_transmitter_config: cctp_message_transmitter_config.to_account_info(), token_messenger: cctp_token_messenger.to_account_info(), remote_token_messenger: cctp_remote_token_messenger.to_account_info(), @@ -386,8 +348,8 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - Custodian::SIGNER_SEEDS, &[ common::CCTP_MESSAGE_SEED_PREFIX, - auction_info.key.as_ref(), - &[data.cctp_message_bump], + auction_placeholder_info.key.as_ref(), + &[new_cctp_message_bump], ], ], ), @@ -400,28 +362,39 @@ pub fn process(accounts: &[AccountInfo], data: &SettleAuctionNoneCctpShimData) - wormhole_message_nonce: common::WORMHOLE_MESSAGE_NONCE, payload: fill.to_vec(), }, - post_message_accounts, + PostMessageAccounts { + emitter: custodian_info.key, + payer: payer_info.key, + message: post_shim_message.key, + sequence: core_bridge_emitter_sequence.key, + }, accounts, )?; - // ------------------------------------------------------------------------------------------------ - // End of burning and posting the message - // Begin of closing the prepared order response - // ------------------------------------------------------------------------------------------------ + // Close the custody token account. let close_token_account_ix = spl_token::instruction::close_account( &spl_token::ID, - closed_prepared_order_response_custody_token_info.key, - closed_prepared_order_response_actor_info.key, + &expected_prepared_custody_key, + &prepared_order_response.prepared_by, custodian_info.key, &[], )?; + invoke_signed_unchecked( &close_token_account_ix, accounts, &[&Custodian::SIGNER_SEEDS], )?; - // ------------------------------------------------------------------------------------------------ - // End of closing the prepared order response + + // Moving the lamports from the prepared order response back to the original + // preparer. The prepared order response account should be closed after this + // point. + let mut prepared_order_response_info_lamports = + prepared_order_response_info.lamports.borrow_mut(); + **original_preparer_info.lamports.borrow_mut() = original_preparer_info + .lamports() + .saturating_add(**prepared_order_response_info_lamports); + **prepared_order_response_info_lamports = 0; Ok(()) } From 2af2c6bfbedb5f6cf28c61f6d58a8087fe6336a4 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 15 May 2025 10:49:47 -0500 Subject: [PATCH 112/112] solana: update IDLs --- solana/ts/src/idl/json/matching_engine.json | 8 ++++++++ solana/ts/src/idl/ts/matching_engine.ts | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/solana/ts/src/idl/json/matching_engine.json b/solana/ts/src/idl/json/matching_engine.json index e86dc99b3..2324b9148 100644 --- a/solana/ts/src/idl/json/matching_engine.json +++ b/solana/ts/src/idl/json/matching_engine.json @@ -3079,6 +3079,10 @@ "code": 7066, "name": "BestOfferTokenMismatch" }, + { + "code": 7067, + "name": "InitialOfferTokenMismatch" + }, { "code": 7068, "name": "BestOfferTokenRequired" @@ -3127,6 +3131,10 @@ "code": 7280, "name": "CannotCloseAuctionYet" }, + { + "code": 7281, + "name": "InvalidFeeRecipientToken" + }, { "code": 7282, "name": "AuctionHistoryNotFull" diff --git a/solana/ts/src/idl/ts/matching_engine.ts b/solana/ts/src/idl/ts/matching_engine.ts index 7f2d76875..744870a2b 100644 --- a/solana/ts/src/idl/ts/matching_engine.ts +++ b/solana/ts/src/idl/ts/matching_engine.ts @@ -3085,6 +3085,10 @@ export type MatchingEngine = { "code": 7066, "name": "bestOfferTokenMismatch" }, + { + "code": 7067, + "name": "initialOfferTokenMismatch" + }, { "code": 7068, "name": "bestOfferTokenRequired" @@ -3133,6 +3137,10 @@ export type MatchingEngine = { "code": 7280, "name": "cannotCloseAuctionYet" }, + { + "code": 7281, + "name": "invalidFeeRecipientToken" + }, { "code": 7282, "name": "auctionHistoryNotFull"