diff --git a/.github/workflows/checks-all.yml b/.github/workflows/checks-all.yml index 8c041ea50..779d1b436 100755 --- a/.github/workflows/checks-all.yml +++ b/.github/workflows/checks-all.yml @@ -345,57 +345,53 @@ jobs: forge test --fork-url https://ethereum-sepolia-rpc.publicnode.com -vv " - ## any use of Move CLI requires ubuntu-24.04 and to not run on buildjet - # bridge-client-integration: - # if: github.event.label.name == 'cicd:bridge' || github.ref == 'refs/heads/main' - # strategy: - # matrix: - # include: - # - os: ubuntu-24.04 - # arch: x86_64 - # runs-on: ubuntu-24.04 - # - # runs-on: ${{ matrix.runs-on }} - # - # steps: - # - name: Checkout repository - # uses: actions/checkout@v4 - # - # - name: Install Movement CLI - # run: | - # sudo apt-get update - # sudo apt-get install -y build-essential - # sudo apt-get install -y binutils - # sudo apt-get install -y lld - # sudo apt-get install -y libudev-dev - # sudo apt-get install -y libdw-dev - # which ld - # which lld - # which gcc - # which cc - # echo $PATH - # export GIT_CLONE_PROTECTION_ACTIVE=false - # git clone https://github.com/movementlabsxyz/aptos-core/ - # cd aptos-core - # cargo build -p movement - # sudo cp target/debug/movement /usr/local/bin/ - # cd - - # - # - name: Install Nix - # uses: DeterminateSystems/nix-installer-action@main - # - # - uses: cachix/cachix-action@v15 - # with: - # name: movementlabs - # authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - # - # - name: Run eth_movement tests - # run: | - # nix develop --command bash -c "rust_backtrace=1 cargo test --test eth_movement -- --nocapture --test-threads=1" - # - # - name: Run movement_eth tests - # run: | - # nix develop --command bash -c "rust_backtrace=1 cargo test --test movement_eth -- --nocapture --test-threads=1" +# any use of Move CLI requires ubuntu-24.04 and to not run on buildjet +bridge-client-integration: + if: github.event.label.name == 'cicd:bridge' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/feature/trusted-relayer' + strategy: + matrix: + include: + - os: ubuntu-24.04 + arch: x86_64 + runs-on: ubuntu-24.04 + runs-on: ${{ matrix.runs-on }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Movement CLI + run: | + sudo apt-get update + sudo apt-get install -y build-essential + sudo apt-get install -y binutils + sudo apt-get install -y lld + sudo apt-get install -y libudev-dev + sudo apt-get install -y libdw-dev + which ld + which lld + which gcc + which cc + echo $PATH + export GIT_CLONE_PROTECTION_ACTIVE=false + git clone https://github.com/movementlabsxyz/aptos-core/ + cd aptos-core + cargo build -p movement + sudo cp target/debug/movement /usr/local/bin/ + cd - + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - uses: cachix/cachix-action@v15 + with: + name: movementlabs + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: Run eth_movement tests + run: | + nix develop --command bash -c "rust_backtrace=1 cargo test --test client_eth_tests -- --nocapture --test-threads=1" + - name: Run movement_eth tests + run: | + nix develop --command bash -c "rust_backtrace=1 cargo test --test client_mvt_tests -- --nocapture --test-threads=1" + - name: Run Relayer tests + run: | + nix develop --command bash -c "rust_backtrace=1 cargo test --test relayer -- --nocapture --test-threads=1" # Indexer: # strategy: @@ -416,4 +412,4 @@ jobs: # # - name: Run Indexer tests in nix environment # # adjust the log level while debugging -# run: CELESTIA_LOG_LEVEL=FATAL nix develop --command bash -c "just movement-full-node native build.celestia-local.indexer.hasura.indexer-test -t=false" \ No newline at end of file +# run: CELESTIA_LOG_LEVEL=FATAL nix develop --command bash -c "just movement-full-node native build.celestia-local.indexer.hasura.indexer-test -t=false" diff --git a/.gitmodules b/.gitmodules index ceb0d21ba..6a2a13a79 100644 --- a/.gitmodules +++ b/.gitmodules @@ -70,3 +70,9 @@ [submodule "protocol-units/tokens/mock/testnet/holesky/lib/openzeppelin-contracts"] path = protocol-units/tokens/mock/testnet/holesky/lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/BokkyPooBahsDateTimeLibrary"] + path = lib/BokkyPooBahsDateTimeLibrary + url = https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary +[submodule "protocol-units/bridge/contracts/lib/BokkyPooBahsDateTimeLibrary"] + path = protocol-units/bridge/contracts/lib/BokkyPooBahsDateTimeLibrary + url = https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary diff --git a/Cargo.lock b/Cargo.lock index 43734f0c6..a98a4caab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "abstract-domain-derive" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "proc-macro2", "quote", @@ -812,7 +812,7 @@ checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "aptos-abstract-gas-usage" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-gas-algebra", @@ -836,7 +836,7 @@ dependencies = [ [[package]] name = "aptos-accumulator" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -846,7 +846,7 @@ dependencies = [ [[package]] name = "aptos-aggregator" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-logger", "aptos-types", @@ -860,7 +860,7 @@ dependencies = [ [[package]] name = "aptos-api" version = "0.2.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-api-types", @@ -902,7 +902,7 @@ dependencies = [ [[package]] name = "aptos-api-types" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-config", @@ -932,7 +932,7 @@ dependencies = [ [[package]] name = "aptos-bcs-utils" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "hex", @@ -941,7 +941,7 @@ dependencies = [ [[package]] name = "aptos-bitvec" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "serde", "serde_bytes", @@ -950,7 +950,7 @@ dependencies = [ [[package]] name = "aptos-block-executor" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-aggregator", @@ -985,7 +985,7 @@ dependencies = [ [[package]] name = "aptos-block-partitioner" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-logger", @@ -1006,7 +1006,7 @@ dependencies = [ [[package]] name = "aptos-bounded-executor" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "futures", "rustversion", @@ -1016,7 +1016,7 @@ dependencies = [ [[package]] name = "aptos-build-info" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "shadow-rs", ] @@ -1024,7 +1024,7 @@ dependencies = [ [[package]] name = "aptos-cached-packages" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-framework", @@ -1038,7 +1038,7 @@ dependencies = [ [[package]] name = "aptos-channels" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-infallible", @@ -1049,7 +1049,7 @@ dependencies = [ [[package]] name = "aptos-compression" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-logger", "aptos-metrics-core", @@ -1061,7 +1061,7 @@ dependencies = [ [[package]] name = "aptos-config" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "aptos-consensus-types" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-bitvec", @@ -1119,7 +1119,7 @@ dependencies = [ [[package]] name = "aptos-crypto" version = "0.0.3" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aes-gcm", "anyhow", @@ -1172,7 +1172,7 @@ dependencies = [ [[package]] name = "aptos-crypto-derive" version = "0.0.3" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "proc-macro2", "quote", @@ -1182,7 +1182,7 @@ dependencies = [ [[package]] name = "aptos-db" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-accumulator", @@ -1230,7 +1230,7 @@ dependencies = [ [[package]] name = "aptos-db-indexer" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-config", @@ -1250,7 +1250,7 @@ dependencies = [ [[package]] name = "aptos-db-indexer-schemas" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-schemadb", @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "aptos-dkg" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -1295,7 +1295,7 @@ dependencies = [ [[package]] name = "aptos-drop-helper" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-infallible", "aptos-metrics-core", @@ -1306,7 +1306,7 @@ dependencies = [ [[package]] name = "aptos-event-notifications" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-channels", @@ -1322,7 +1322,7 @@ dependencies = [ [[package]] name = "aptos-executor" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-consensus-types", @@ -1354,7 +1354,7 @@ dependencies = [ [[package]] name = "aptos-executor-service" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-block-partitioner", "aptos-config", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "aptos-executor-test-helpers" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-cached-packages", @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "aptos-executor-types" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -1426,7 +1426,7 @@ dependencies = [ [[package]] name = "aptos-experimental-runtimes" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-runtimes", "core_affinity", @@ -1439,7 +1439,7 @@ dependencies = [ [[package]] name = "aptos-faucet-core" version = "2.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-config", @@ -1473,7 +1473,7 @@ dependencies = [ [[package]] name = "aptos-faucet-metrics-server" version = "2.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-logger", @@ -1487,7 +1487,7 @@ dependencies = [ [[package]] name = "aptos-framework" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-aggregator", @@ -1555,7 +1555,7 @@ dependencies = [ [[package]] name = "aptos-gas-algebra" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "either", "move-core-types", @@ -1564,7 +1564,7 @@ dependencies = [ [[package]] name = "aptos-gas-meter" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-algebra", "aptos-gas-schedule", @@ -1579,7 +1579,7 @@ dependencies = [ [[package]] name = "aptos-gas-profiling" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-gas-algebra", @@ -1599,7 +1599,7 @@ dependencies = [ [[package]] name = "aptos-gas-schedule" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-algebra", "aptos-global-constants", @@ -1612,17 +1612,17 @@ dependencies = [ [[package]] name = "aptos-global-constants" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" [[package]] name = "aptos-id-generator" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" [[package]] name = "aptos-indexer" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-api", @@ -1654,7 +1654,7 @@ dependencies = [ [[package]] name = "aptos-indexer-grpc-fullnode" version = "1.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-api", @@ -1666,7 +1666,7 @@ dependencies = [ "aptos-mempool", "aptos-metrics-core", "aptos-moving-average 0.1.0 (git+https://github.com/movementlabsxyz/aptos-indexer-processors)", - "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41)", + "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?branch=movement)", "aptos-runtimes", "aptos-storage-interface", "aptos-types", @@ -1692,7 +1692,7 @@ dependencies = [ [[package]] name = "aptos-indexer-grpc-table-info" version = "1.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-api", @@ -1723,11 +1723,11 @@ dependencies = [ [[package]] name = "aptos-indexer-grpc-utils" version = "1.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-metrics-core", - "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41)", + "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?branch=movement)", "async-trait", "backoff", "base64 0.13.1", @@ -1755,12 +1755,12 @@ dependencies = [ [[package]] name = "aptos-infallible" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" [[package]] name = "aptos-jellyfish-merkle" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -1788,7 +1788,7 @@ dependencies = [ [[package]] name = "aptos-keygen" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-types", @@ -1798,7 +1798,7 @@ dependencies = [ [[package]] name = "aptos-language-e2e-tests" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-abstract-gas-usage", @@ -1842,7 +1842,7 @@ dependencies = [ [[package]] name = "aptos-ledger" version = "0.2.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-types", @@ -1855,7 +1855,7 @@ dependencies = [ [[package]] name = "aptos-log-derive" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "proc-macro2", "quote", @@ -1865,7 +1865,7 @@ dependencies = [ [[package]] name = "aptos-logger" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-infallible", "aptos-log-derive", @@ -1889,7 +1889,7 @@ dependencies = [ [[package]] name = "aptos-memory-usage-tracker" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-algebra", "aptos-gas-meter", @@ -1902,7 +1902,7 @@ dependencies = [ [[package]] name = "aptos-mempool" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-bounded-executor", @@ -1942,7 +1942,7 @@ dependencies = [ [[package]] name = "aptos-mempool-notifications" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-types", "async-trait", @@ -1955,7 +1955,7 @@ dependencies = [ [[package]] name = "aptos-memsocket" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-infallible", "bytes 1.8.0", @@ -1966,7 +1966,7 @@ dependencies = [ [[package]] name = "aptos-metrics-core" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "prometheus", @@ -1975,7 +1975,7 @@ dependencies = [ [[package]] name = "aptos-move-stdlib" version = "0.1.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-schedule", "aptos-native-interface", @@ -2006,7 +2006,7 @@ dependencies = [ [[package]] name = "aptos-mvhashmap" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-aggregator", @@ -2027,7 +2027,7 @@ dependencies = [ [[package]] name = "aptos-native-interface" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-algebra", "aptos-gas-schedule", @@ -2044,7 +2044,7 @@ dependencies = [ [[package]] name = "aptos-netcore" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-memsocket", "aptos-proxy", @@ -2061,7 +2061,7 @@ dependencies = [ [[package]] name = "aptos-network" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-bitvec", @@ -2106,7 +2106,7 @@ dependencies = [ [[package]] name = "aptos-node-identity" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-types", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "aptos-node-resource-metrics" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-build-info", "aptos-infallible", @@ -2133,7 +2133,7 @@ dependencies = [ [[package]] name = "aptos-num-variants" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "proc-macro2", "quote", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "aptos-openapi" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "async-trait", "percent-encoding", @@ -2156,7 +2156,7 @@ dependencies = [ [[package]] name = "aptos-package-builder" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-framework", @@ -2169,7 +2169,7 @@ dependencies = [ [[package]] name = "aptos-peer-monitoring-service-types" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-config", "aptos-types", @@ -2194,7 +2194,7 @@ dependencies = [ [[package]] name = "aptos-proptest-helpers" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "crossbeam", "proptest", @@ -2204,7 +2204,7 @@ dependencies = [ [[package]] name = "aptos-protos" version = "1.3.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=338f9a1bcc06f62ce4a4994f1642b9a61b631ee0#338f9a1bcc06f62ce4a4994f1642b9a61b631ee0" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "futures-core", "pbjson", @@ -2216,7 +2216,7 @@ dependencies = [ [[package]] name = "aptos-protos" version = "1.3.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?rev=338f9a1bcc06f62ce4a4994f1642b9a61b631ee0#338f9a1bcc06f62ce4a4994f1642b9a61b631ee0" dependencies = [ "futures-core", "pbjson", @@ -2228,7 +2228,7 @@ dependencies = [ [[package]] name = "aptos-proxy" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "ipnet", ] @@ -2236,7 +2236,7 @@ dependencies = [ [[package]] name = "aptos-push-metrics" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-logger", "aptos-metrics-core", @@ -2247,7 +2247,7 @@ dependencies = [ [[package]] name = "aptos-resource-viewer" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-types", @@ -2261,7 +2261,7 @@ dependencies = [ [[package]] name = "aptos-rest-client" version = "0.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-api-types", @@ -2284,7 +2284,7 @@ dependencies = [ [[package]] name = "aptos-rocksdb-options" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-config", "rocksdb", @@ -2293,7 +2293,7 @@ dependencies = [ [[package]] name = "aptos-runtimes" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "rayon", "tokio", @@ -2302,7 +2302,7 @@ dependencies = [ [[package]] name = "aptos-schemadb" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-infallible", @@ -2319,7 +2319,7 @@ dependencies = [ [[package]] name = "aptos-scratchpad" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-drop-helper", @@ -2338,7 +2338,7 @@ dependencies = [ [[package]] name = "aptos-sdk" version = "0.0.3" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-cached-packages", @@ -2360,7 +2360,7 @@ dependencies = [ [[package]] name = "aptos-sdk-builder" version = "0.2.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-types", @@ -2378,11 +2378,11 @@ dependencies = [ [[package]] name = "aptos-secure-net" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-logger", "aptos-metrics-core", - "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41)", + "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?branch=movement)", "bcs 0.1.4", "crossbeam-channel", "once_cell", @@ -2396,7 +2396,7 @@ dependencies = [ [[package]] name = "aptos-secure-storage" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-infallible", @@ -2417,7 +2417,7 @@ dependencies = [ [[package]] name = "aptos-short-hex-str" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "mirai-annotations", "serde", @@ -2428,7 +2428,7 @@ dependencies = [ [[package]] name = "aptos-speculative-state-helper" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-infallible", @@ -2439,7 +2439,7 @@ dependencies = [ [[package]] name = "aptos-storage-interface" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-crypto", @@ -2487,7 +2487,7 @@ dependencies = [ [[package]] name = "aptos-table-natives" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-gas-schedule", "aptos-native-interface", @@ -2505,7 +2505,7 @@ dependencies = [ [[package]] name = "aptos-temppath" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "hex", "rand 0.7.3", @@ -2514,7 +2514,7 @@ dependencies = [ [[package]] name = "aptos-time-service" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-infallible", "enum_dispatch", @@ -2527,7 +2527,7 @@ dependencies = [ [[package]] name = "aptos-types" version = "0.0.3" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-bitvec", @@ -2584,12 +2584,12 @@ dependencies = [ [[package]] name = "aptos-utils" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" [[package]] name = "aptos-vault-client" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "base64 0.13.1", @@ -2605,7 +2605,7 @@ dependencies = [ [[package]] name = "aptos-vm" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-aggregator", @@ -2655,7 +2655,7 @@ dependencies = [ [[package]] name = "aptos-vm-genesis" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-cached-packages", "aptos-crypto", @@ -2676,7 +2676,7 @@ dependencies = [ [[package]] name = "aptos-vm-logging" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "aptos-crypto", "aptos-logger", @@ -2691,7 +2691,7 @@ dependencies = [ [[package]] name = "aptos-vm-types" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-aggregator", @@ -2713,7 +2713,7 @@ dependencies = [ [[package]] name = "aptos-vm-validator" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "aptos-logger", @@ -4202,13 +4202,17 @@ version = "0.0.2" dependencies = [ "anyhow", "bigdecimal", + "bridge-config", "bridge-util", "chrono", "diesel", "diesel_migrations", + "godfig", "hex", "serde", "tokio", + "tokio-stream", + "tracing", ] [[package]] @@ -4218,6 +4222,7 @@ dependencies = [ "alloy", "alloy-contract", "alloy-network", + "alloy-primitives 0.7.7", "alloy-sol-types", "anyhow", "aptos-framework", @@ -4230,8 +4235,10 @@ dependencies = [ "bridge-grpc", "bridge-service", "bridge-setup", + "bridge-util", "chrono", "dot-movement", + "ethabi", "futures", "godfig", "hex", @@ -4239,6 +4246,7 @@ dependencies = [ "rand 0.7.3", "reqwest 0.12.9", "serde_json", + "tiny-keccak", "tokio", "tokio-stream", "tonic 0.12.3", @@ -4326,13 +4334,17 @@ name = "bridge-util" version = "0.0.2" dependencies = [ "alloy", + "anyhow", "async-trait", "derive_more 0.99.18", + "futures", "hex", "rand 0.7.3", "serde", + "tokio", "thiserror 1.0.69", "tokio-stream", + "tracing", ] [[package]] @@ -9080,7 +9092,7 @@ dependencies = [ "aptos-language-e2e-tests", "aptos-logger", "aptos-mempool", - "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41)", + "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?branch=movement)", "aptos-sdk", "aptos-storage-interface", "aptos-temppath", @@ -9449,7 +9461,7 @@ checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" [[package]] name = "move-abigen" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bcs 0.1.4", @@ -9466,7 +9478,7 @@ dependencies = [ [[package]] name = "move-binary-format" version = "0.0.3" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "backtrace", @@ -9481,12 +9493,12 @@ dependencies = [ [[package]] name = "move-borrow-graph" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" [[package]] name = "move-bytecode-source-map" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bcs 0.1.4", @@ -9501,7 +9513,7 @@ dependencies = [ [[package]] name = "move-bytecode-spec" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "once_cell", "quote", @@ -9511,7 +9523,7 @@ dependencies = [ [[package]] name = "move-bytecode-utils" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "move-binary-format", @@ -9523,7 +9535,7 @@ dependencies = [ [[package]] name = "move-bytecode-verifier" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "fail", "move-binary-format", @@ -9537,7 +9549,7 @@ dependencies = [ [[package]] name = "move-bytecode-viewer" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "clap 4.5.21", @@ -9552,7 +9564,7 @@ dependencies = [ [[package]] name = "move-cli" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "clap 4.5.21", @@ -9582,7 +9594,7 @@ dependencies = [ [[package]] name = "move-command-line-common" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "difference", @@ -9599,7 +9611,7 @@ dependencies = [ [[package]] name = "move-compiler" version = "0.0.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bcs 0.1.4", @@ -9625,7 +9637,7 @@ dependencies = [ [[package]] name = "move-compiler-v2" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "abstract-domain-derive", "anyhow", @@ -9656,7 +9668,7 @@ dependencies = [ [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "arbitrary", @@ -9681,7 +9693,7 @@ dependencies = [ [[package]] name = "move-coverage" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bcs 0.1.4", @@ -9700,7 +9712,7 @@ dependencies = [ [[package]] name = "move-disassembler" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "clap 4.5.21", @@ -9717,7 +9729,7 @@ dependencies = [ [[package]] name = "move-docgen" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "clap 4.5.21", @@ -9736,7 +9748,7 @@ dependencies = [ [[package]] name = "move-errmapgen" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "move-command-line-common", @@ -9748,7 +9760,7 @@ dependencies = [ [[package]] name = "move-ir-compiler" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bcs 0.1.4", @@ -9764,7 +9776,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "codespan-reporting", @@ -9782,7 +9794,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode-syntax" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "hex", @@ -9795,7 +9807,7 @@ dependencies = [ [[package]] name = "move-ir-types" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "hex", "move-command-line-common", @@ -9808,7 +9820,7 @@ dependencies = [ [[package]] name = "move-model" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "codespan", @@ -9834,7 +9846,7 @@ dependencies = [ [[package]] name = "move-package" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "clap 4.5.21", @@ -9868,7 +9880,7 @@ dependencies = [ [[package]] name = "move-prover" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "atty", @@ -9895,7 +9907,7 @@ dependencies = [ [[package]] name = "move-prover-boogie-backend" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "async-trait", @@ -9924,7 +9936,7 @@ dependencies = [ [[package]] name = "move-prover-bytecode-pipeline" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "abstract-domain-derive", "anyhow", @@ -9941,7 +9953,7 @@ dependencies = [ [[package]] name = "move-resource-viewer" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "hex", @@ -9968,7 +9980,7 @@ dependencies = [ [[package]] name = "move-stackless-bytecode" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "abstract-domain-derive", "codespan-reporting", @@ -9987,7 +9999,7 @@ dependencies = [ [[package]] name = "move-stdlib" version = "0.1.1" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "hex", @@ -10010,7 +10022,7 @@ dependencies = [ [[package]] name = "move-symbol-pool" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "once_cell", "serde", @@ -10019,7 +10031,7 @@ dependencies = [ [[package]] name = "move-table-extension" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "better_any", "bytes 1.8.0", @@ -10034,7 +10046,7 @@ dependencies = [ [[package]] name = "move-unit-test" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "better_any", @@ -10062,7 +10074,7 @@ dependencies = [ [[package]] name = "move-vm-runtime" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "better_any", "bytes 1.8.0", @@ -10086,7 +10098,7 @@ dependencies = [ [[package]] name = "move-vm-test-utils" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "anyhow", "bytes 1.8.0", @@ -10101,7 +10113,7 @@ dependencies = [ [[package]] name = "move-vm-types" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41#9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" +source = "git+https://github.com/movementlabsxyz/aptos-core?branch=movement#6b0d39326878fb813e1b2b9e8ba1ff4f24a4cbc1" dependencies = [ "bcs 0.1.4", "derivative", @@ -10310,7 +10322,7 @@ name = "movement-client" version = "0.0.2" dependencies = [ "anyhow", - "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=9dfc8e7a3d622597dfd81cc4ba480a5377f87a41)", + "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?branch=movement)", "aptos-sdk", "aptos-types", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index e044247fc..ed6d36f8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ members = [ "protocol-units/bridge/service", "protocol-units/bridge/grpc", "protocol-units/bridge/integration-tests", - "protocol-units/bridge/indexer-db", + #"protocol-units/bridge/indexer-db", "protocol-units/bridge/util", "benches/*", "util/signing/interface", @@ -142,47 +142,54 @@ serde_yaml = "0.9.34" ## Aptos dependencies ### We use a forked version so that we can override dependency versions. This is required ### to be avoid dependency conflicts with other Sovereign Labs crates. -aptos-api = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-api-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-bitvec = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-block-executor = { git = "https://github.com/movementlabsxyz/aptos-core.git", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-cached-packages = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-config = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-consensus-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-crypto = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41", features = [ +aptos-api = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-api-types = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-bitvec = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-block-executor = { git = "https://github.com/movementlabsxyz/aptos-core.git", branch = "movement" } +aptos-cached-packages = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-config = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-consensus-types = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-crypto = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement", features = [ "cloneable-private-keys", ] } -aptos-db = { git = "https://github.com/movementlabsxyz/aptos-core.git", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-executor = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-executor-test-helpers = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-executor-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-faucet-core = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-framework = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-language-e2e-tests = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-mempool = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-proptest-helpers = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-sdk = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-state-view = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-storage-interface = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-temppath = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-vm = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-vm-genesis = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-vm-logging = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-vm-validator = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-logger = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-vm-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-indexer = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-indexer-grpc-fullnode = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-indexer-grpc-table-info = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } -aptos-protos = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "9dfc8e7a3d622597dfd81cc4ba480a5377f87a41" } +aptos-db = { git = "https://github.com/movementlabsxyz/aptos-core.git", branch = "movement" } +aptos-executor = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-executor-test-helpers = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-executor-types = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-faucet-core = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-framework = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-language-e2e-tests = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-mempool = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-proptest-helpers = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-sdk = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-state-view = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-storage-interface = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-temppath = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-types = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-vm = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-vm-genesis = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-vm-logging = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-vm-validator = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-logger = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-vm-types = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-indexer = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-indexer-grpc-fullnode = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-indexer-grpc-table-info = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } +aptos-protos = { git = "https://github.com/movementlabsxyz/aptos-core", branch = "movement" } # Indexer processor = { git = "https://github.com/movementlabsxyz/aptos-indexer-processors", rev = "7658338eb9224abd9698c1e02dbc6ca526c268ce" } server-framework = { git = "https://github.com/movementlabsxyz/aptos-indexer-processors", rev = "7658338eb9224abd9698c1e02dbc6ca526c268ce" } bcs = { git = "https://github.com/aptos-labs/bcs.git", rev = "d31fab9d81748e2594be5cd5cdf845786a30562d" } - +ethereum-types = "0.14.1" +ethers = "=2.0.10" +ethers-core = { version = "=2.0.10", default-features = false } +ethers-contract = "=2.0.10" +ethers-providers = { version = "=2.0.10", default-features = false } +ethers-signers = { version = "=2.0.10", default-features = false } +ethers-middleware = { version = "=2.0.10", default-features = false } +ethabi = "18.0.0" move-binary-format = { git = "https://github.com/diem/move" } move-table-extension = { git = "https://github.com/diem/move" } move-core-types = { git = "https://github.com/diem/move" } @@ -355,3 +362,4 @@ opt-level = 3 [patch.crates-io] merlin = { git = "https://github.com/aptos-labs/merlin" } x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek", branch = "zeroize_v1" } + diff --git a/process-compose/bridge/process-compose.bridge-indexer.yml b/process-compose/bridge/process-compose.bridge-indexer.yml index 55a6999fd..fb29872f3 100644 --- a/process-compose/bridge/process-compose.bridge-indexer.yml +++ b/process-compose/bridge/process-compose.bridge-indexer.yml @@ -11,9 +11,13 @@ processes: exec: command: echo "true" - bridge: + bridge_indexer: + command: | + RUST_BACKTRACE=1 start_indexer depends_on: postgres: condition: process_healthy + bridge: + condition: process_healthy environment: - BRIDGE_INDEXER_DATABASE_URL="postgresql://postgres:password@localhost:5432" diff --git a/process-compose/bridge/process-compose.setup.yml b/process-compose/bridge/process-compose.setup.yml index 584037d94..36c8b5d00 100644 --- a/process-compose/bridge/process-compose.setup.yml +++ b/process-compose/bridge/process-compose.setup.yml @@ -15,7 +15,7 @@ processes: - "MAYBE_DEPLOY_MCR=true" command: | - movement-full-node-setup + movement-full-node-setup depends_on: build: condition: process_completed_successfully diff --git a/process-compose/bridge/process-compose.yml b/process-compose/bridge/process-compose.yml index 585c75ede..803baa9b1 100644 --- a/process-compose/bridge/process-compose.yml +++ b/process-compose/bridge/process-compose.yml @@ -43,7 +43,7 @@ processes: movement-full-node: command: | - movement-full-node + movement-full-node run depends_on: movement-celestia-da-light-node: condition: process_healthy diff --git a/process-compose/movement-full-node/process-compose.yml b/process-compose/movement-full-node/process-compose.yml index cbc89bc9d..c898284e7 100644 --- a/process-compose/movement-full-node/process-compose.yml +++ b/process-compose/movement-full-node/process-compose.yml @@ -59,7 +59,7 @@ processes: movement-full-node: condition: process_healthy readiness_probe: - initial_delay_seconds: 10 + initial_delay_seconds: 30 failure_threshold: 12 exec: command: curl http://0.0.0.0:30732 diff --git a/protocol-units/bridge/config/src/common/eth.rs b/protocol-units/bridge/config/src/common/eth.rs index df8c8005b..8f8f22e3b 100644 --- a/protocol-units/bridge/config/src/common/eth.rs +++ b/protocol-units/bridge/config/src/common/eth.rs @@ -9,8 +9,7 @@ const DEFAULT_ETH_RPC_CONNECTION_HOSTNAME: &str = "localhost"; const DEFAULT_ETH_RPC_CONNECTION_PORT: u16 = 8545; const DEFAULT_ETH_WS_CONNECTION_HOSTNAME: &str = "localhost"; const DEFAULT_ETH_WS_CONNECTION_PORT: u16 = 8545; // same as RPC -const DEFAULT_ETH_INITIATOR_CONTRACT: &str = "Oxeee"; -const DEFAULT_ETH_COUNTERPARTY_CONTRACT: &str = "0xccc"; +const DEFAULT_ETH_NATIVE_CONTRACT: &str = "Oxeee"; const DEFAULT_ETH_WETH_CONTRACT: &str = "0xe3e3"; const DEFAULT_ETH_MOVETOKEN_CONTRACT: &str = "0xe3e2"; const DEFAULT_ASSET: &str = "MOVE"; @@ -34,10 +33,8 @@ pub struct EthConfig { #[serde(default)] // Eth chain config. pub eth_chain_id: u64, - #[serde(default = "default_eth_initiator_contract")] - pub eth_initiator_contract: String, - #[serde(default = "default_eth_counterparty_contract")] - pub eth_counterparty_contract: String, + #[serde(default = "default_eth_native_contract")] + pub eth_native_contract: String, #[serde(default = "default_eth_weth_contract")] pub eth_weth_contract: String, #[serde(default = "default_eth_move_token_contract")] @@ -46,9 +43,6 @@ pub struct EthConfig { #[serde(default = "default_signer_private_key")] pub signer_private_key: String, - #[serde(default = "default_time_lock_secs")] - pub time_lock_secs: u64, - #[serde(default = "default_gas_limit")] pub gas_limit: u64, #[serde(default = "default_transaction_send_retries")] @@ -69,17 +63,10 @@ env_default!( ); env_default!( - default_eth_initiator_contract, - "ETH_INITIATOR_CONTRACT", + default_eth_native_contract, + "ETH_NATIVE_CONTRACT", String, - DEFAULT_ETH_INITIATOR_CONTRACT.to_string() -); - -env_default!( - default_eth_counterparty_contract, - "ETH_COUNTERPARTY_CONTRACT", - String, - DEFAULT_ETH_COUNTERPARTY_CONTRACT.to_string() + DEFAULT_ETH_NATIVE_CONTRACT.to_string() ); env_default!( @@ -97,8 +84,6 @@ env_default!( env_default!(default_asset, "ASSET", String, DEFAULT_ASSET.to_string()); -env_short_default!(default_time_lock_secs, u64, 48 * 60 * 60 as u64); //48h by default - env_short_default!(default_gas_limit, u64, 10_000_000_000_000_000 as u64); env_short_default!(default_transaction_send_retries, u32, 10 as u32); @@ -185,13 +170,10 @@ impl Default for EthConfig { eth_ws_connection_port: default_eth_ws_connection_port(), eth_chain_id: default_eth_chain_id(), - eth_initiator_contract: default_eth_initiator_contract(), - eth_counterparty_contract: default_eth_counterparty_contract(), + eth_native_contract: default_eth_native_contract(), eth_weth_contract: default_eth_weth_contract(), eth_move_token_contract: default_eth_move_token_contract(), - time_lock_secs: default_time_lock_secs(), - signer_private_key: default_signer_private_key(), gas_limit: default_gas_limit(), transaction_send_retries: default_transaction_send_retries(), diff --git a/protocol-units/bridge/config/src/common/indexer.rs b/protocol-units/bridge/config/src/common/indexer.rs new file mode 100644 index 000000000..4626e9cf4 --- /dev/null +++ b/protocol-units/bridge/config/src/common/indexer.rs @@ -0,0 +1,41 @@ +use godfig::env_default; +use serde::{Deserialize, Serialize}; + +const DEFAULT_REST_LISTENER_HOSTNAME: &str = "0.0.0.0"; +const DEFAULT_REST_LISTENER_PORT: u16 = 30884; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IndexerConfig { + /// URL for the bridge indexer database + #[serde(default = "default_database_url")] + pub indexer_url: String, + + /// Endpoint for the REST service + #[serde(default = "default_rest_listener_hostname")] + pub rest_listener_hostname: String, + #[serde(default = "default_rest_listener_port")] + pub rest_port: u16, +} + +impl Default for IndexerConfig { + fn default() -> Self { + Self { + indexer_url: default_database_url(), + rest_listener_hostname: default_rest_listener_hostname(), + rest_port: default_rest_listener_port(), + } + } +} + +fn default_database_url() -> String { + "postgresql://postgres:password@localhost:5432".to_string() +} + +env_default!( + default_rest_listener_hostname, + "REST_LISTENER_HOSTNAME", + String, + DEFAULT_REST_LISTENER_HOSTNAME.to_string() +); + +env_default!(default_rest_listener_port, "REST_LISTENER_PORT", u16, DEFAULT_REST_LISTENER_PORT); diff --git a/protocol-units/bridge/config/src/common/mod.rs b/protocol-units/bridge/config/src/common/mod.rs index 2b3fd55b8..886c880e2 100644 --- a/protocol-units/bridge/config/src/common/mod.rs +++ b/protocol-units/bridge/config/src/common/mod.rs @@ -1,4 +1,5 @@ pub mod eth; +pub mod indexer; pub mod movement; pub mod testing; diff --git a/protocol-units/bridge/config/src/lib.rs b/protocol-units/bridge/config/src/lib.rs index a7ecd4a8c..4985391e5 100644 --- a/protocol-units/bridge/config/src/lib.rs +++ b/protocol-units/bridge/config/src/lib.rs @@ -17,6 +17,10 @@ pub struct Config { /// Optional testing config #[serde(default)] pub testing: common::testing::TestingConfig, + + /// Database configuration for the bridge indexer + #[serde(default)] + pub indexer: common::indexer::IndexerConfig, } impl Default for Config { @@ -25,6 +29,7 @@ impl Default for Config { eth: common::eth::EthConfig::default(), movement: common::movement::MovementConfig::default(), testing: common::testing::TestingConfig::default(), + indexer: common::indexer::IndexerConfig::default(), } } } @@ -41,6 +46,7 @@ impl Config { eth: common::eth::EthConfig::default(), movement: common::movement::MovementConfig::for_test(), testing: common::testing::TestingConfig::default(), + indexer: common::indexer::IndexerConfig::default(), } } } diff --git a/protocol-units/bridge/contracts/remappings.txt b/protocol-units/bridge/contracts/remappings.txt index cba72cb70..b20cd1281 100644 --- a/protocol-units/bridge/contracts/remappings.txt +++ b/protocol-units/bridge/contracts/remappings.txt @@ -1,5 +1,9 @@ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ -ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/ -erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ +@DateTimeLibrary/=lib/BokkyPooBahsDateTimeLibrary/ +ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/ +erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/ forge-std/=lib/forge-std/src/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ \ No newline at end of file +halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/ +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ +openzeppelin-contracts/=lib/openzeppelin-contracts/ diff --git a/protocol-units/bridge/contracts/src/AtomicBridgeCounterpartyMOVE.sol b/protocol-units/bridge/contracts/src/AtomicBridgeCounterpartyMOVE.sol deleted file mode 100644 index b48b6f833..000000000 --- a/protocol-units/bridge/contracts/src/AtomicBridgeCounterpartyMOVE.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.22; - -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import {IAtomicBridgeCounterpartyMOVE} from "./IAtomicBridgeCounterpartyMOVE.sol"; -import {AtomicBridgeInitiatorMOVE} from "./AtomicBridgeInitiatorMOVE.sol"; -import {RateLimiter} from "./RateLimiter.sol"; - -contract AtomicBridgeCounterpartyMOVE is IAtomicBridgeCounterpartyMOVE, OwnableUpgradeable { - enum MessageState { - PENDING, - COMPLETED, - REFUNDED - } - - struct BridgeTransferDetails { - bytes32 originator; - address recipient; - uint256 amount; - bytes32 hashLock; - uint256 timeLock; - MessageState state; - } - - AtomicBridgeInitiatorMOVE public atomicBridgeInitiatorMOVE; - RateLimiter public rateLimiter; - mapping(bytes32 => BridgeTransferDetails) public bridgeTransfers; - - // Configurable time lock duration - uint256 public counterpartyTimeLockDuration; - - function initialize(address _atomicBridgeInitiator, address owner, uint256 _timeLockDuration) public initializer { - if (_atomicBridgeInitiator == address(0)) revert ZeroAddress(); - atomicBridgeInitiatorMOVE = AtomicBridgeInitiatorMOVE(_atomicBridgeInitiator); - __Ownable_init(owner); - - // Set the configurable time lock duration - counterpartyTimeLockDuration = _timeLockDuration; - } - - function setAtomicBridgeInitiator(address _atomicBridgeInitiator) external onlyOwner { - if (_atomicBridgeInitiator == address(0)) revert ZeroAddress(); - atomicBridgeInitiatorMOVE = AtomicBridgeInitiatorMOVE(_atomicBridgeInitiator); - } - - function setTimeLockDuration(uint256 _timeLockDuration) external onlyOwner { - counterpartyTimeLockDuration = _timeLockDuration; - } - - function setRateLimiter(address _rateLimiter) external onlyOwner { - if (_rateLimiter == address(0)) revert ZeroAddress(); - rateLimiter = RateLimiter(_rateLimiter); - } - - function lockBridgeTransfer( - bytes32 originator, - bytes32 bridgeTransferId, - bytes32 hashLock, - address recipient, - uint256 amount - ) external onlyOwner { - if (amount == 0) revert ZeroAmount(); - - // The time lock is now based on the configurable duration - uint256 timeLock = block.timestamp + counterpartyTimeLockDuration; - - bridgeTransfers[bridgeTransferId] = BridgeTransferDetails({ - recipient: recipient, - originator: originator, - amount: amount, - hashLock: hashLock, - timeLock: timeLock, - state: MessageState.PENDING - }); - - emit BridgeTransferLocked(bridgeTransferId, recipient, amount, hashLock, counterpartyTimeLockDuration); - } - - function completeBridgeTransfer(bytes32 bridgeTransferId, bytes32 preImage) external { - BridgeTransferDetails storage details = bridgeTransfers[bridgeTransferId]; - if (details.state != MessageState.PENDING) revert BridgeTransferStateNotPending(); - bytes32 computedHash = keccak256(abi.encodePacked(preImage)); - if (computedHash != details.hashLock) revert InvalidSecret(); - if (block.timestamp > details.timeLock) revert TimeLockExpired(); - - details.state = MessageState.COMPLETED; - - emit BridgeTransferCompleted(bridgeTransferId, preImage); - } - - function abortBridgeTransfer(bytes32 bridgeTransferId) external onlyOwner { - BridgeTransferDetails storage details = bridgeTransfers[bridgeTransferId]; - if (details.state != MessageState.PENDING) revert BridgeTransferStateNotPending(); - if (block.timestamp <= details.timeLock) revert TimeLockNotExpired(); - - details.state = MessageState.REFUNDED; - - emit BridgeTransferAborted(bridgeTransferId); - } -} diff --git a/protocol-units/bridge/contracts/src/AtomicBridgeInitiatorMOVE.sol b/protocol-units/bridge/contracts/src/AtomicBridgeInitiatorMOVE.sol deleted file mode 100644 index 3bff65592..000000000 --- a/protocol-units/bridge/contracts/src/AtomicBridgeInitiatorMOVE.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.22; - -import {IAtomicBridgeInitiatorMOVE} from "./IAtomicBridgeInitiatorMOVE.sol"; -import {MockMOVEToken} from "./MockMOVEToken.sol"; -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {RateLimiter} from "./RateLimiter.sol"; - -contract AtomicBridgeInitiatorMOVE is IAtomicBridgeInitiatorMOVE, OwnableUpgradeable { - enum MessageState { - INITIALIZED, - COMPLETED, - REFUNDED - } - - struct BridgeTransfer { - uint256 amount; - address originator; - bytes32 recipient; - bytes32 hashLock; - uint256 timeLock; // in seconds (timestamp) - MessageState state; - } - - // Mapping of bridge transfer ids to BridgeTransfer structs - mapping(bytes32 => BridgeTransfer) public bridgeTransfers; - - // Total MOVE token pool balance - uint256 public poolBalance; - - address public counterpartyAddress; - RateLimiter public rateLimiter; - ERC20Upgradeable public moveToken; - uint256 private nonce; - - // Configurable time lock duration - uint256 public initiatorTimeLockDuration; - - // Initialize the contract with MOVE token address, owner, custom time lock duration, and initial pool balance - function initialize( - address _moveToken, - address owner, - uint256 _timeLockDuration, - uint256 _initialPoolBalance - ) public initializer { - require(_moveToken != address(0) && owner != address(0), "ZeroAddress"); - moveToken = ERC20Upgradeable(_moveToken); - __Ownable_init(owner); - - // Set the custom time lock duration - initiatorTimeLockDuration = _timeLockDuration; - - // Set the initial pool balance - poolBalance = _initialPoolBalance; - } - - function setCounterpartyAddress(address _counterpartyAddress) external onlyOwner { - require(_counterpartyAddress != address(0), "ZeroAddress"); - counterpartyAddress = _counterpartyAddress; - } - - function setRateLimiter(address _rateLimiter) external onlyOwner { - if (_rateLimiter == address(0)) revert ZeroAddress(); - rateLimiter = RateLimiter(_rateLimiter); - } - - function initiateBridgeTransfer(uint256 moveAmount, bytes32 recipient, bytes32 hashLock) - external - returns (bytes32 bridgeTransferId) - { - rateLimiter.rateLimitOutbound(moveAmount); - address originator = msg.sender; - - require(moveAmount > 0, "ZeroAmount"); - - // Ensure there is a valid amount - if (moveAmount == 0) { - revert ZeroAmount(); - } - - // Transfer the MOVE tokens from the user to the contract - if (!moveToken.transferFrom(originator, address(this), moveAmount)) { - revert MOVETransferFailed(); - } - - // Update the pool balance - poolBalance += moveAmount; - - // Generate a unique nonce to prevent replay attacks, and generate a transfer ID - bridgeTransferId = keccak256(abi.encodePacked(originator, recipient, hashLock, initiatorTimeLockDuration, block.timestamp, nonce++)); - - bridgeTransfers[bridgeTransferId] = BridgeTransfer({ - amount: moveAmount, - originator: originator, - recipient: recipient, - hashLock: hashLock, - timeLock: block.timestamp + initiatorTimeLockDuration, - state: MessageState.INITIALIZED - }); - - emit BridgeTransferInitiated(bridgeTransferId, originator, recipient, moveAmount, hashLock, initiatorTimeLockDuration); - return bridgeTransferId; - } - - function completeBridgeTransfer(bytes32 bridgeTransferId, bytes32 preImage) external onlyOwner { - BridgeTransfer storage bridgeTransfer = bridgeTransfers[bridgeTransferId]; - - rateLimiter.rateLimitInbound(bridgeTransfer.amount); - require(bridgeTransfer.state == MessageState.INITIALIZED, "BridgeTransferHasBeenCompleted"); - require(keccak256(abi.encodePacked(preImage)) == bridgeTransfer.hashLock, "InvalidSecret"); - require(block.timestamp <= bridgeTransfer.timeLock, "TimelockExpired"); - - bridgeTransfer.state = MessageState.COMPLETED; - - emit BridgeTransferCompleted(bridgeTransferId, preImage); - } - - function refundBridgeTransfer(bytes32 bridgeTransferId) external onlyOwner { - BridgeTransfer storage bridgeTransfer = bridgeTransfers[bridgeTransferId]; - rateLimiter.rateLimitInbound(bridgeTransfer.amount); - require(bridgeTransfer.state == MessageState.INITIALIZED, "BridgeTransferStateNotInitialized"); - require(block.timestamp >= bridgeTransfer.timeLock, "TimeLockNotExpired"); - - bridgeTransfer.state = MessageState.REFUNDED; - - if (!moveToken.transfer(bridgeTransfer.originator, bridgeTransfer.amount)) revert("MOVETransferFailed"); - - emit BridgeTransferRefunded(bridgeTransferId); - } - - function withdrawMOVE(address recipient, uint256 amount) external { - if (msg.sender != counterpartyAddress) revert Unauthorized(); - if (poolBalance < amount) revert InsufficientMOVEBalance(); - poolBalance -= amount; - if (!moveToken.transfer(recipient, amount)) revert MOVETransferFailed(); - } -} diff --git a/protocol-units/bridge/contracts/src/IAtomicBridgeCounterpartyMOVE.sol b/protocol-units/bridge/contracts/src/IAtomicBridgeCounterpartyMOVE.sol deleted file mode 100644 index 8d531c00f..000000000 --- a/protocol-units/bridge/contracts/src/IAtomicBridgeCounterpartyMOVE.sol +++ /dev/null @@ -1,59 +0,0 @@ -pragma solidity ^0.8.22; - -interface IAtomicBridgeCounterpartyMOVE { - // Event emitted when a new atomic bridge transfer is locked - event BridgeTransferLocked( - bytes32 indexed bridgeTransferId, address indexed recipient, uint256 amount, bytes32 hashLock, uint256 timeLock - ); - - // Event emitted when a BridgeTransfer is completed - event BridgeTransferCompleted(bytes32 indexed bridgeTransferId, bytes32 pre_image); - - // Event emitted when a BridgeTransfer is aborted - event BridgeTransferAborted(bytes32 indexed bridgeTransferId); - - error ZeroAmount(); - error MOVETransferFailed(); - error BridgeTransferInvalid(); - error InvalidSecret(); - error BridgeTransferHasBeenCompleted(); - error BridgeTransferStateNotInitialized(); - error BridgeTransferStateNotPending(); - error InsufficientMOVEBalance(); - error TimeLockExpired(); - error TimeLockNotExpired(); - error ZeroAddress(); - error Unauthorized(); - - /** - * @dev Locks the assets for a new atomic bridge transfer - * @param initiator The address of the initiator of the BridgeTransfer - * @param bridgeTransferId A unique id representing this BridgeTransfer - * @param hashLock The hash of the secret (HASH) that will unlock the funds - * @param recipient The address to which to transfer the funds - * @param amount The amount of WETH to lock - * - */ - function lockBridgeTransfer( - bytes32 initiator, - bytes32 bridgeTransferId, - bytes32 hashLock, - address recipient, - uint256 amount - ) external; - - /** - * @dev Completes the bridge transfer and withdraws WETH to the recipient - * @param bridgeTransferId Unique identifier for the BridgeTransfer - * @param preImage The secret that unlocks the funds - * - */ - function completeBridgeTransfer(bytes32 bridgeTransferId, bytes32 preImage) external; - - /** - * @dev Cancels the bridge transfer and refunds the initiator if the timelock has expired - * @param bridgeTransferId Unique identifier for the BridgeTransfer - * - */ - function abortBridgeTransfer(bytes32 bridgeTransferId) external; -} diff --git a/protocol-units/bridge/contracts/src/IAtomicBridgeInitiatorMOVE.sol b/protocol-units/bridge/contracts/src/IAtomicBridgeInitiatorMOVE.sol deleted file mode 100644 index 45ffc2c61..000000000 --- a/protocol-units/bridge/contracts/src/IAtomicBridgeInitiatorMOVE.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.22; - -interface IAtomicBridgeInitiatorMOVE { - // Event emitted when a new atomic bridge transfer is created - event BridgeTransferInitiated( - bytes32 indexed _bridgeTransferId, - address indexed _originator, - bytes32 indexed _recipient, - uint256 amount, - bytes32 _hashLock, - uint256 _timeLock - ); - // Event emitted when a BridgeTransfer is completed (withdrawn) - event BridgeTransferCompleted(bytes32 indexed _bridgeTransferId, bytes32 pre_image); - // Event emitted when a BridgeTransfer is refunded - event BridgeTransferRefunded(bytes32 indexed _bridgeTransferId); - - error ZeroAmount(); - error MOVETransferFailed(); - error BridgeTransferInvalid(); - error InvalidSecret(); - error BridgeTransferHasBeenCompleted(); - error BridgeTransferStateNotInitialized(); - error InsufficientMOVEBalance(); - error TimeLockNotExpired(); - error TimelockExpired(); - error ZeroAddress(); - error Unauthorized(); - - - /** - * @dev Creates a new atomic bridge transfer using native ETH - * @param _wethAmount The amount of WETH to send - * @param _recipient The address on the other chain to which to transfer the funds - * @param _hashLock The hash of the secret (HASH) that will unlock the funds - * @return _bridgeTransferId A unique id representing this BridgeTransfer - * - */ - function initiateBridgeTransfer(uint256 _wethAmount, bytes32 _recipient, bytes32 _hashLock) - external - returns (bytes32 _bridgeTransferId); - - /** - * @dev Completes the bridging Counterparty - * @param _bridgeTransferId Unique identifier for the BridgeTransfer - * @param preImage The secret that unlocks the funds - * - */ - function completeBridgeTransfer(bytes32 _bridgeTransferId, bytes32 preImage) external; - - /** - * @dev Refunds the funds back to the initiator if the timelock has expired - * @param _bridgeTransferId Unique identifier for the BridgeTransfer - * - */ - function refundBridgeTransfer(bytes32 _bridgeTransferId) external; -} diff --git a/protocol-units/bridge/contracts/src/INativeBridge.sol b/protocol-units/bridge/contracts/src/INativeBridge.sol index 6fb1f4f34..3e21a9d9c 100644 --- a/protocol-units/bridge/contracts/src/INativeBridge.sol +++ b/protocol-units/bridge/contracts/src/INativeBridge.sol @@ -19,12 +19,20 @@ interface INativeBridge { uint256 nonce ); + event InsuranceFundUpdated(address insuranceFund); + event PauseToggled(bool paused); + event RiskDenominatorUpdated(uint256 riskDenominator); + error ZeroAmount(); error MOVETransferFailed(); error ZeroAddress(); error InvalidLenghts(); error InvalidBridgeTransferId(); error CompletedBridgeTransferId(); + error InvalidNonce(); + error OutboundRateLimitExceeded(); + error InboundRateLimitExceeded(); + error InvalidRiskDenominator(); /** * @dev Creates a new bridge diff --git a/protocol-units/bridge/contracts/src/NativeBridge.sol b/protocol-units/bridge/contracts/src/NativeBridge.sol index f4650900b..1b25451d0 100644 --- a/protocol-units/bridge/contracts/src/NativeBridge.sol +++ b/protocol-units/bridge/contracts/src/NativeBridge.sol @@ -6,40 +6,67 @@ import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/acce import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {INativeBridge} from "./INativeBridge.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -// import {RateLimiter} from "./RateLimiter.sol"; +import "forge-std/console.sol"; contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeBridge { - struct OutgoingBridgeTransfer { + struct OutboundTransfer { + bytes32 bridgeTransferId; address initiator; bytes32 recipient; uint256 amount; - uint256 nonce; } - mapping(bytes32 bridgeTransferId => OutgoingBridgeTransfer) public outgoingBridgeTransfers; - mapping(uint256 nonce => bytes32 incomingBridgeTransferId) public noncesToIncomingBridgeTransferIds; - IERC20 public moveToken; + mapping(uint256 nonce => OutboundTransfer) public noncesToOutboundTransfers; + mapping(bytes32 bridgeTransferId => uint256 nonce) public idsToInboundNonces; + mapping(uint256 day => uint256 amount) public inboundRateLimitBudget; + bytes32 public constant RELAYER_ROLE = keccak256(abi.encodePacked("RELAYER_ROLE")); + uint256 public constant MINIMUM_RISK_DENOMINATOR = 3; + IERC20 public moveToken; + address public insuranceFund; + uint256 public riskDenominator; uint256 private _nonce; // Prevents initialization of implementation contract exploits constructor() { _disableInitializers(); } - // TODO: include rate limit - function initialize(address _moveToken, address _admin, address _relayer, address _maintainer) public initializer { + /** + * @dev Initializes the NativeBridge contract + * @param _moveToken The address of the MOVE token contract + * @param _admin The address of the admin role + * @param _relayer The address of the relayer role + * @param _maintainer The address of the maintainer role + */ + function initialize( + address _moveToken, + address _admin, + address _relayer, + address _maintainer, + address _insuranceFund + ) public initializer { require(_moveToken != address(0) && _admin != address(0) && _relayer != address(0), ZeroAddress()); __Pausable_init(); moveToken = IERC20(_moveToken); _grantRole(DEFAULT_ADMIN_ROLE, _admin); _grantRole(RELAYER_ROLE, _relayer); + // Set insurance fund + insuranceFund = _insuranceFund; + riskDenominator = MINIMUM_RISK_DENOMINATOR + 1; + // Maintainer is optional _grantRole(RELAYER_ROLE, _maintainer); } + /** + * @dev Creates a new bridge + * @param recipient The address on the other chain to which to transfer funds + * @param amount The amount of MOVE to send + * @return bridgeTransferId A unique id representing this BridgeTransfer + * + */ function initiateBridgeTransfer(bytes32 recipient, uint256 amount) external whenNotPaused @@ -47,7 +74,6 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB { // Ensure there is a valid amount require(amount > 0, ZeroAmount()); - // _l1l2RateLimit(amount); address initiator = msg.sender; // Transfer the MOVE tokens from the user to the contract @@ -57,12 +83,20 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB bridgeTransferId = keccak256(abi.encodePacked(initiator, recipient, amount, ++_nonce)); // Store the bridge transfer details - outgoingBridgeTransfers[bridgeTransferId] = OutgoingBridgeTransfer(initiator, recipient, amount, _nonce); + noncesToOutboundTransfers[_nonce] = OutboundTransfer(bridgeTransferId, initiator, recipient, amount); emit BridgeTransferInitiated(bridgeTransferId, initiator, recipient, amount, _nonce); return bridgeTransferId; } + /** + * @dev Completes the bridging of funds. Only the relayer can call this function. + * @param bridgeTransferId Unique identifier for the BridgeTransfer + * @param initiator The address on the other chain that originated the transfer of funds + * @param recipient The address on this chain to which to transfer funds + * @param amount The amount to transfer + * @param nonce The seed nonce to generate the bridgeTransferId + */ function completeBridgeTransfer( bytes32 bridgeTransferId, bytes32 initiator, @@ -73,6 +107,14 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB _completeBridgeTransfer(bridgeTransferId, initiator, recipient, amount, nonce); } + /** + * @dev Completes multiple bridge transfers + * @param bridgeTransferIds Unique identifiers for the BridgeTransfers + * @param initiators The addresses on the other chain that originated the transfer of funds + * @param recipients The addresses on this chain to which to transfer funds + * @param amounts The amounts to transfer + * @param nonces The seed nonces to generate the bridgeTransferIds + */ function batchCompleteBridgeTransfer( bytes32[] memory bridgeTransferIds, bytes32[] memory initiators, @@ -100,17 +142,18 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB uint256 amount, uint256 nonce ) internal { - // _l2l1RateLimit(amount); + _rateLimitInbound(amount); + // Ensure the bridge transfer has not already been completed + require(nonce > 0, InvalidNonce()); + require(idsToInboundNonces[bridgeTransferId] == 0, CompletedBridgeTransferId()); // Ensure the bridge transfer ID is valid against the initiator, recipient, amount, and nonce require( bridgeTransferId == keccak256(abi.encodePacked(initiator, recipient, amount, nonce)), InvalidBridgeTransferId() ); - // Ensure the bridge transfer has not already been completed - require(noncesToIncomingBridgeTransferIds[nonce] == bytes32(0x0), CompletedBridgeTransferId()); // Store the nonce to bridge transfer ID - noncesToIncomingBridgeTransferIds[nonce] = bridgeTransferId; + idsToInboundNonces[bridgeTransferId] = nonce; // Transfer the MOVE tokens to the recipient if (!moveToken.transfer(recipient, amount)) revert MOVETransferFailed(); @@ -118,7 +161,46 @@ contract NativeBridge is AccessControlUpgradeable, PausableUpgradeable, INativeB emit BridgeTransferCompleted(bridgeTransferId, initiator, recipient, amount, nonce); } + /** + * @dev Sets the insurance fund address for rate limiting purposes + * @param _insuranceFund The new insurance fund address + */ + function setInsuranceFund(address _insuranceFund) external onlyRole(DEFAULT_ADMIN_ROLE) { + require(_insuranceFund != address(0), ZeroAddress()); + insuranceFund = _insuranceFund; + emit InsuranceFundUpdated(_insuranceFund); + } + + /** + * @dev Sets the risk denominator for the bridge + * @param _riskDenominator The new risk denominator + */ + function setRiskDenominator(uint256 _riskDenominator) external onlyRole(DEFAULT_ADMIN_ROLE) { + // risk denominator must be at least 4 + require(_riskDenominator > MINIMUM_RISK_DENOMINATOR, InvalidRiskDenominator()); + riskDenominator = _riskDenominator; + emit RiskDenominatorUpdated(_riskDenominator); + } + + /** + * @dev Toggles the paused state of the contract + */ function togglePause() external onlyRole(DEFAULT_ADMIN_ROLE) { paused() ? _pause() : _unpause(); + emit PauseToggled(paused()); + } + + /** + * @dev Rate limits the inbound transfers based on the insurance fund and risk denominator + * @param amount The amount to rate limit + */ + + function _rateLimitInbound(uint256 amount) public { + uint256 day = block.timestamp / 1 days; + inboundRateLimitBudget[day] += amount; + require( + inboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / riskDenominator, + InboundRateLimitExceeded() + ); } } diff --git a/protocol-units/bridge/contracts/src/RateLimiter.sol b/protocol-units/bridge/contracts/src/RateLimiter.sol deleted file mode 100644 index 5c5f618b5..000000000 --- a/protocol-units/bridge/contracts/src/RateLimiter.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; - -import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract RateLimiter is AccessControlUpgradeable { - bytes32 public constant ATOMIC_BRIDGE = keccak256("ATOMIC_BRIDGE"); - - mapping(uint256 day => uint256 amount) public outboundRateLimitBudget; - mapping(uint256 day => uint256 amount) public inboundRateLimitBudget; - address public insuranceFund; - IERC20 public moveToken; - - error OutboundRateLimitExceeded(); - error InboundRateLimitExceeded(); - - constructor() { - _disableInitializers(); - } - - function initialize( - address _moveToken, - address _owner, - address _initiatorAddress, - address _counterpartyAddress, - address _insuranceFund - ) public initializer { - _grantRole(DEFAULT_ADMIN_ROLE, _owner); - _grantRole(ATOMIC_BRIDGE, _initiatorAddress); - _grantRole(ATOMIC_BRIDGE, _counterpartyAddress); - moveToken = IERC20(_moveToken); - insuranceFund = _insuranceFund; - } - - function rateLimitOutbound(uint256 amount) external onlyRole(ATOMIC_BRIDGE) { - uint256 day = block.timestamp / 1 days; - outboundRateLimitBudget[day] += amount; - require(outboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / 4, OutboundRateLimitExceeded()); - } - - function rateLimitInbound(uint256 amount) external onlyRole(ATOMIC_BRIDGE) { - uint256 day = block.timestamp / 1 days; - inboundRateLimitBudget[day] += amount; - require(inboundRateLimitBudget[day] < moveToken.balanceOf(insuranceFund) / 4, InboundRateLimitExceeded()); - } -} diff --git a/protocol-units/bridge/contracts/test/AtomicBridgeCounterpartyMOVE.t.sol b/protocol-units/bridge/contracts/test/AtomicBridgeCounterpartyMOVE.t.sol deleted file mode 100644 index 91cbe7e17..000000000 --- a/protocol-units/bridge/contracts/test/AtomicBridgeCounterpartyMOVE.t.sol +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; -pragma abicoder v2; - -import {Test, console} from "forge-std/Test.sol"; -import {AtomicBridgeCounterpartyMOVE} from "../src/AtomicBridgeCounterpartyMOVE.sol"; -import {AtomicBridgeInitiatorMOVE} from "../src/AtomicBridgeInitiatorMOVE.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {MockMOVEToken} from "../src/MockMOVEToken.sol"; -import {RateLimiter} from "../src/RateLimiter.sol"; - -contract AtomicBridgeCounterpartyMOVETest is Test { - AtomicBridgeCounterpartyMOVE public atomicBridgeCounterpartyMOVEImplementation; - AtomicBridgeCounterpartyMOVE public atomicBridgeCounterpartyMOVE; - AtomicBridgeInitiatorMOVE public atomicBridgeInitiatorMOVEImplementation; - AtomicBridgeInitiatorMOVE public atomicBridgeInitiatorMOVE; - RateLimiter public rateLimiterImplementation; - RateLimiter public rateLimiter; - MockMOVEToken public moveToken; - TransparentUpgradeableProxy public proxy; - - address public deployer = address(0x1); - address public originator = address(1); - address public recipient = address(0x2); - address public otherUser = address(0x3); - address public insuranceFund = address(0x4); - bytes32 public hashLock = keccak256(abi.encodePacked("secret")); - uint256 public amount = 100 * 10 ** 8; // 100 MOVEToken (assuming 8 decimals) - uint256 public timeLock = 100; - bytes32 public initiator = keccak256(abi.encodePacked(deployer)); - bytes32 public bridgeTransferId = - keccak256(abi.encodePacked(block.timestamp, initiator, recipient, amount, hashLock, timeLock)); - - uint256 public constant COUNTERPARTY_TIME_LOCK_DURATION = 24 * 60 * 60; // 24 hours - - function setUp() public { - // Set the counterparty contract in the AtomicBridgeInitiator contract - // Deploy the MOVEToken contract and mint some tokens to the deployer - moveToken = new MockMOVEToken(); - moveToken.initialize(address(this)); // Contract will hold initial MOVE tokens - - moveToken.transfer(insuranceFund, moveToken.balanceOf(address(this)) / 10); // 10% of the total supply - - // Time lock durations - uint256 initiatorTimeLockDuration = 48 * 60 * 60; // 48 hours for the initiator - uint256 counterpartyTimeLockDuration = 24 * 60 * 60; // 24 hours for the counterparty - - originator = vm.addr(uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)))); - - // Deploy the AtomicBridgeInitiator contract with a 48-hour time lock - atomicBridgeInitiatorMOVEImplementation = new AtomicBridgeInitiatorMOVE(); - proxy = new TransparentUpgradeableProxy( - address(atomicBridgeInitiatorMOVEImplementation), - address(deployer), - abi.encodeWithSignature( - "initialize(address,address,uint256,uint256)", - address(moveToken), - deployer, - initiatorTimeLockDuration, - 0 ether // Initial pool balance - ) - ); - atomicBridgeInitiatorMOVE = AtomicBridgeInitiatorMOVE(address(proxy)); - - // Deploy the AtomicBridgeCounterparty contract with a 24-hour time lock - atomicBridgeCounterpartyMOVEImplementation = new AtomicBridgeCounterpartyMOVE(); - proxy = new TransparentUpgradeableProxy( - address(atomicBridgeCounterpartyMOVEImplementation), - address(deployer), - abi.encodeWithSignature( - "initialize(address,address,uint256)", - address(atomicBridgeInitiatorMOVE), - deployer, - counterpartyTimeLockDuration - ) - ); - atomicBridgeCounterpartyMOVE = AtomicBridgeCounterpartyMOVE(address(proxy)); - - rateLimiterImplementation = new RateLimiter(); - proxy = new TransparentUpgradeableProxy( - address(rateLimiterImplementation), - address(deployer), - abi.encodeWithSignature( - "initialize(address,address,address,address,address)", - address(moveToken), - deployer, - address(atomicBridgeInitiatorMOVE), - address(atomicBridgeCounterpartyMOVE), - insuranceFund - ) - ); - - rateLimiter = RateLimiter(address(proxy)); - - vm.startPrank(deployer); - atomicBridgeInitiatorMOVE.setRateLimiter(address(rateLimiter)); - atomicBridgeCounterpartyMOVE.setRateLimiter(address(rateLimiter)); - - atomicBridgeInitiatorMOVE.setCounterpartyAddress(address(atomicBridgeCounterpartyMOVE)); - vm.stopPrank(); - } - - function testLockBridgeTransfer() public { - uint256 moveAmount = 100 * 10 ** 8; - moveToken.transfer(originator, moveAmount); - vm.startPrank(originator); - - // Approve the AtomicBridgeInitiatorMOVE contract to spend MOVEToken - moveToken.approve(address(atomicBridgeInitiatorMOVE), amount); - - // Initiate the bridge transfer - atomicBridgeInitiatorMOVE.initiateBridgeTransfer(amount, initiator, hashLock); - - vm.stopPrank(); - - vm.startPrank(deployer); // Only the owner (deployer) can call lockBridgeTransfer - atomicBridgeCounterpartyMOVE.lockBridgeTransfer(initiator, bridgeTransferId, hashLock, recipient, amount); - vm.stopPrank(); - - ( - bytes32 pendingInitiator, - address pendingRecipient, - uint256 pendingAmount, - bytes32 pendingHashLock, - uint256 pendingTimelock, - AtomicBridgeCounterpartyMOVE.MessageState pendingState - ) = atomicBridgeCounterpartyMOVE.bridgeTransfers(bridgeTransferId); - - assertEq(pendingInitiator, initiator); - assertEq(pendingRecipient, recipient); - assertEq(pendingAmount, amount); - assertEq(pendingHashLock, hashLock); - assertGt(pendingTimelock, block.timestamp); - assertEq(uint8(pendingState), uint8(AtomicBridgeCounterpartyMOVE.MessageState.PENDING)); - } - - function testCompleteBridgeTransfer() public { - bytes32 preImage = "secret"; - bytes32 testHashLock = keccak256(abi.encodePacked(preImage)); - - uint256 moveAmount = 100 * 10 ** 8; - moveToken.transfer(originator, moveAmount); - vm.startPrank(originator); - - // Approve the AtomicBridgeInitiatorMOVE contract to spend MOVEToken - moveToken.approve(address(atomicBridgeInitiatorMOVE), amount); - - // Initiate the bridge transfer - atomicBridgeInitiatorMOVE.initiateBridgeTransfer(amount, initiator, testHashLock); - - vm.stopPrank(); - - vm.startPrank(deployer); // Only the owner (deployer) can call lockBridgeTransfer - atomicBridgeCounterpartyMOVE.lockBridgeTransfer(initiator, bridgeTransferId, testHashLock, recipient, amount); - vm.stopPrank(); - - vm.startPrank(otherUser); - - atomicBridgeCounterpartyMOVE.completeBridgeTransfer(bridgeTransferId, preImage); - - ( - bytes32 completedInitiator, - address completedRecipient, - uint256 completedAmount, - bytes32 completedHashLock, - uint256 completedTimeLock, - AtomicBridgeCounterpartyMOVE.MessageState completedState - ) = atomicBridgeCounterpartyMOVE.bridgeTransfers(bridgeTransferId); - - assertEq(completedInitiator, initiator); - assertEq(completedRecipient, recipient); - assertEq(completedAmount, amount); - assertEq(completedHashLock, testHashLock); - assertGt(completedTimeLock, block.timestamp); - assertEq(uint8(completedState), uint8(AtomicBridgeCounterpartyMOVE.MessageState.COMPLETED)); - - vm.stopPrank(); - } - - function testAbortBridgeTransfer() public { - uint256 moveAmount = 100 * 10 ** 8; - moveToken.transfer(originator, moveAmount); - vm.startPrank(originator); - - // Approve the AtomicBridgeInitiatorMOVE contract to spend MOVEToken - moveToken.approve(address(atomicBridgeInitiatorMOVE), amount); - - // Initiate the bridge transfer - atomicBridgeInitiatorMOVE.initiateBridgeTransfer(amount, initiator, hashLock); - - vm.stopPrank(); - - vm.startPrank(deployer); - - atomicBridgeCounterpartyMOVE.lockBridgeTransfer(initiator, bridgeTransferId, hashLock, recipient, amount); - - vm.stopPrank(); - - // Advance the block number to beyond the timelock period - vm.warp(block.timestamp + COUNTERPARTY_TIME_LOCK_DURATION + 1); - - // Try to abort as a malicious user (this should fail) - //vm.startPrank(otherUser); - //vm.expectRevert("Ownable: caller is not the owner"); - //atomicBridgeCounterpartyMOVE.abortBridgeTransfer(bridgeTransferId); - //vm.stopPrank(); - - // Abort as the owner (this should pass) - vm.startPrank(deployer); // The deployer is the owner - atomicBridgeCounterpartyMOVE.abortBridgeTransfer(bridgeTransferId); - - ( - bytes32 abortedInitiator, - address abortedRecipient, - uint256 abortedAmount, - bytes32 abortedHashLock, - uint256 abortedTimeLock, - AtomicBridgeCounterpartyMOVE.MessageState abortedState - ) = atomicBridgeCounterpartyMOVE.bridgeTransfers(bridgeTransferId); - - assertEq(abortedInitiator, initiator); - assertEq(abortedRecipient, recipient); - assertEq(abortedAmount, amount); - assertEq(abortedHashLock, hashLock); - assertLe(abortedTimeLock, block.timestamp, "Timelock is not less than or equal to current timestamp"); - assertEq(uint8(abortedState), uint8(AtomicBridgeCounterpartyMOVE.MessageState.REFUNDED)); - - vm.stopPrank(); - } -} diff --git a/protocol-units/bridge/contracts/test/AtomicBridgeInitiatorMOVE.t.sol b/protocol-units/bridge/contracts/test/AtomicBridgeInitiatorMOVE.t.sol deleted file mode 100644 index 4da72b316..000000000 --- a/protocol-units/bridge/contracts/test/AtomicBridgeInitiatorMOVE.t.sol +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; -pragma abicoder v2; - -import {Test, console} from "forge-std/Test.sol"; -import {AtomicBridgeInitiatorMOVE, IAtomicBridgeInitiatorMOVE, OwnableUpgradeable} from "../src/AtomicBridgeInitiatorMOVE.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {MockMOVEToken} from "../src/MockMOVEToken.sol"; -import {RateLimiter} from "../src/RateLimiter.sol"; - -contract AtomicBridgeInitiatorMOVETest is Test { - AtomicBridgeInitiatorMOVE public atomicBridgeInitiatorImplementation; - MockMOVEToken public moveToken; - RateLimiter public rateLimiterImplementation; - RateLimiter public rateLimiter; - ProxyAdmin public proxyAdmin; - TransparentUpgradeableProxy public proxy; - AtomicBridgeInitiatorMOVE public atomicBridgeInitiatorMOVE; - - address public originator = address(1); - address public insuranceFund = address(4); - bytes32 public recipient = keccak256(abi.encodePacked(address(2))); - bytes32 public hashLock = keccak256(abi.encodePacked("secret")); - uint256 public amount = 1 ether; - uint256 public constant timeLockDuration = 48 * 60 * 60; // 48 hours in seconds - - function setUp() public { - // Deploy the MOVEToken contract and mint some tokens to the deployer - moveToken = new MockMOVEToken(); - moveToken.initialize(address(this)); // Contract will hold initial MOVE tokens - moveToken.transfer(insuranceFund, moveToken.balanceOf(address(this)) / 10); // 10% of the total supply - - originator = vm.addr(uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)))); - - // Deploy the AtomicBridgeInitiatorMOVE contract - atomicBridgeInitiatorImplementation = new AtomicBridgeInitiatorMOVE(); - proxyAdmin = new ProxyAdmin(msg.sender); - proxy = new TransparentUpgradeableProxy( - address(atomicBridgeInitiatorImplementation), - address(proxyAdmin), - abi.encodeWithSignature( - "initialize(address,address,uint256,uint256)", - address(moveToken), - address(this), - timeLockDuration, - 0 ether - ) - ); - - atomicBridgeInitiatorMOVE = AtomicBridgeInitiatorMOVE(address(proxy)); - - rateLimiterImplementation = new RateLimiter(); - proxy = new TransparentUpgradeableProxy( - address(rateLimiterImplementation), - address(this), - abi.encodeWithSignature( - "initialize(address,address,address,address,address)", - address(moveToken), - address(this), - address(atomicBridgeInitiatorMOVE), - address(0x1337), // just a mock address - insuranceFund - ) - ); - - rateLimiter = RateLimiter(address(proxy)); - - atomicBridgeInitiatorMOVE.setRateLimiter(address(rateLimiter)); - } - - function testInitiateBridgeTransferWithMove() public { - uint256 moveAmount = 100 * 10**8; - - // Transfer moveAmount tokens to the originator and check initial balance - moveToken.transfer(originator, moveAmount); - uint256 initialBalance = moveToken.balanceOf(originator); - - vm.startPrank(originator); - moveToken.approve(address(atomicBridgeInitiatorMOVE), moveAmount); - - // Initiate the bridge transfer - bytes32 bridgeTransferId = atomicBridgeInitiatorMOVE.initiateBridgeTransfer( - moveAmount, - recipient, - hashLock - ); - - // Verify the bridge transfer details - ( - uint256 transferAmount, - address transferOriginator, - bytes32 transferRecipient, - bytes32 transferHashLock, - uint256 transferTimeLock, - AtomicBridgeInitiatorMOVE.MessageState transferState - ) = atomicBridgeInitiatorMOVE.bridgeTransfers(bridgeTransferId); - - assertEq(transferAmount, moveAmount); - assertEq(transferOriginator, originator); - assertEq(transferRecipient, recipient); - assertEq(transferHashLock, hashLock); - assertGt(transferTimeLock, block.timestamp); - assertEq(uint8(transferState), uint8(AtomicBridgeInitiatorMOVE.MessageState.INITIALIZED)); - - // Check the originator's MOVE balance after initiating the transfer - uint256 finalBalance = moveToken.balanceOf(originator); - assertEq(finalBalance, initialBalance - moveAmount); - - vm.stopPrank(); - } - - function testCompleteBridgeTransfer() public { - bytes32 secret = "secret"; - bytes32 testHashLock = keccak256(abi.encodePacked(secret)); - uint256 moveAmount = 100 * 10**8; // 100 MOVEToken - - // Transfer moveAmount tokens to the originator and check initial balance - moveToken.transfer(originator, moveAmount); - uint256 initialBalance = moveToken.balanceOf(originator); - - vm.startPrank(originator); - moveToken.approve(address(atomicBridgeInitiatorMOVE), moveAmount); - - // Initiate the bridge transfer - bytes32 bridgeTransferId = atomicBridgeInitiatorMOVE.initiateBridgeTransfer( - moveAmount, - recipient, - testHashLock - ); - - vm.stopPrank(); - - atomicBridgeInitiatorMOVE.completeBridgeTransfer(bridgeTransferId, secret); - - // Verify the bridge transfer details after completion - ( - uint256 completedAmount, - address completedOriginator, - bytes32 completedRecipient, - bytes32 completedHashLock, - uint256 completedTimeLock, - AtomicBridgeInitiatorMOVE.MessageState completedState - ) = atomicBridgeInitiatorMOVE.bridgeTransfers(bridgeTransferId); - - assertEq(completedAmount, moveAmount); - assertEq(completedOriginator, originator); - assertEq(completedRecipient, recipient); - assertEq(completedHashLock, testHashLock); - assertGt(completedTimeLock, block.timestamp); - assertEq(uint8(completedState), uint8(AtomicBridgeInitiatorMOVE.MessageState.COMPLETED)); - - // Ensure no changes to the originator's balance after the transfer is completed - uint256 finalBalance = moveToken.balanceOf(originator); - assertEq(finalBalance, initialBalance - moveAmount); - } - - function testRefundBridgeTransfer() public { - uint256 moveAmount = 100 * 10**8; // 100 MOVEToken - - // Transfer moveAmount tokens to the originator and check initial balance - moveToken.transfer(originator, moveAmount); - uint256 initialBalance = moveToken.balanceOf(originator); - - vm.startPrank(originator); - moveToken.approve(address(atomicBridgeInitiatorMOVE), moveAmount); - - // Initiate the bridge transfer - bytes32 bridgeTransferId = atomicBridgeInitiatorMOVE.initiateBridgeTransfer( - moveAmount, - recipient, - hashLock - ); - vm.stopPrank(); - - // Advance time and block height to ensure the time lock has expired - vm.warp(block.timestamp + timeLockDuration + 1); - - // Test that a non-owner cannot call refund - vm.startPrank(originator); - vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, originator)); - atomicBridgeInitiatorMOVE.refundBridgeTransfer(bridgeTransferId); - vm.stopPrank(); - - // Owner refunds the transfer - vm.expectEmit(); - emit IAtomicBridgeInitiatorMOVE.BridgeTransferRefunded(bridgeTransferId); - atomicBridgeInitiatorMOVE.refundBridgeTransfer(bridgeTransferId); - - // Verify that the originator receives the refund and the balance is restored - uint256 finalBalance = moveToken.balanceOf(originator); - assertEq(finalBalance, initialBalance, "MOVE balance mismatch"); - } -} - diff --git a/protocol-units/bridge/contracts/test/NativeBridge.t.sol b/protocol-units/bridge/contracts/test/NativeBridge.t.sol index 1464a4705..2604c64b7 100644 --- a/protocol-units/bridge/contracts/test/NativeBridge.t.sol +++ b/protocol-units/bridge/contracts/test/NativeBridge.t.sol @@ -22,6 +22,7 @@ contract NativeBridgeTest is Test { address public relayer = address(0x8e1a7e8); address public recipient = address(0x2); address public otherUser = address(0x3); + address public insuranceFund = address(0x4); function setUp() public { moveToken = new MockMOVEToken(); @@ -33,10 +34,16 @@ contract NativeBridgeTest is Test { address(nativeBridgeImplementation), address(proxyAdmin), abi.encodeWithSignature( - "initialize(address,address,address,address)", address(moveToken), deployer, relayer, address(0) + "initialize(address,address,address,address,address)", + address(moveToken), + deployer, + relayer, + address(0), + insuranceFund ) ); nativeBridge = NativeBridge(address(proxy)); + moveToken.transfer(insuranceFund, 1000000 * 1e8); } function testInitiateBridgeFuzz(address _originator, bytes32 _recipient, uint256 _amount) public { @@ -44,7 +51,7 @@ contract NativeBridgeTest is Test { vm.assume(_originator != address(0)); vm.assume(_originator != deployer); - _amount = bound(_amount, 1, 10000000000 * 10 ** 8); + _amount = bound(_amount, 1, 1000000 * 1e8 - 1); moveToken.transfer(_originator, _amount); vm.startPrank(_originator); // require approval @@ -60,13 +67,111 @@ contract NativeBridgeTest is Test { bytes32 bridgeTransferId = nativeBridge.initiateBridgeTransfer(_recipient, _amount); - (address originator, bytes32 recipient_, uint256 amount, uint256 nonce) = - nativeBridge.outgoingBridgeTransfers(bridgeTransferId); + (bytes32 bridgeTransferId_, address originator, bytes32 recipient_, uint256 amount) = + nativeBridge.noncesToOutboundTransfers(1); assertEq(originator, _originator); assertEq(recipient_, _recipient); assertEq(amount, _amount); - assertEq(nonce, 1); + assertEq(bridgeTransferId_, bridgeTransferId); + vm.stopPrank(); + } + + function testInboundRateLimitFuzz(address receiver, uint256 _amount, uint256 _denominator, uint256 _divisor) public { + uint256 insuranceBalance = moveToken.balanceOf(insuranceFund); + + // amount has to be divisible by 4 + _amount = bound(_amount, 3, insuranceBalance); + // fund bridge with sufficient funds + moveToken.transfer(address(nativeBridge), _amount); + // risk denominator has to be between 4 and move total supply + _denominator = bound(_denominator, 4, moveToken.totalSupply()); + _divisor = bound(_divisor, 4, 20); + + uint256 perTransfer = _amount / _divisor; + + bytes32 tx1BridgeTransferId = keccak256(abi.encodePacked(keccak256(abi.encodePacked(receiver)), receiver, perTransfer, uint256(1))); + bytes32 tx2BridgeTransferId = keccak256(abi.encodePacked(keccak256(abi.encodePacked(receiver)), receiver, perTransfer, uint256(2))); + + + vm.startPrank(relayer); + nativeBridge.completeBridgeTransfer(tx1BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 1); + vm.warp(1 days - 1); + + uint256 snapshot = vm.snapshot(); + + // if the sum of the two transfers is below the rate limit, the second transfer should go through, else it fails + if (perTransfer * 2 >= insuranceBalance / 4) { + vm.expectRevert(INativeBridge.InboundRateLimitExceeded.selector); + nativeBridge.completeBridgeTransfer(tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2); + vm.warp(1 days + 1); + if (perTransfer >= insuranceBalance / 4) { + // unable to bridge above rate limit + vm.expectRevert(INativeBridge.InboundRateLimitExceeded.selector); + nativeBridge.completeBridgeTransfer(tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2); + } else { + nativeBridge.completeBridgeTransfer(tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2); + } + } else { + nativeBridge.completeBridgeTransfer(tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2); + } + vm.stopPrank(); + + vm.revertTo(snapshot); + vm.warp(1 days - 1); + vm.prank(deployer); + nativeBridge.setRiskDenominator(_denominator); + vm.startPrank(relayer); + + tx2BridgeTransferId = keccak256(abi.encodePacked(keccak256(abi.encodePacked(receiver)), receiver, perTransfer, uint256(2))); + + // if the sum of the two transfers is below the rate limit with altered denominator, the second transfer should go through, else it fails + if (perTransfer * 2 >= insuranceBalance / _denominator) { + vm.expectRevert(INativeBridge.InboundRateLimitExceeded.selector); + nativeBridge.completeBridgeTransfer( + tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2 + ); + // Warp to the next period and retry the transfer + vm.warp(1 days + 1); + if (perTransfer >= insuranceBalance / _denominator) { + // unable to bridge above rate limit + vm.expectRevert(INativeBridge.InboundRateLimitExceeded.selector); + nativeBridge.completeBridgeTransfer( + tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2 + ); + } else { + nativeBridge.completeBridgeTransfer( + tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2 + ); + } + } else { + // Complete the second transfer if rate limit is not exceeded + nativeBridge.completeBridgeTransfer( + tx2BridgeTransferId, keccak256(abi.encodePacked(receiver)), receiver, perTransfer, 2 + ); + } + vm.stopPrank(); + + vm.revertTo(snapshot); + vm.warp(1 days - 1); + // test accumulation against the rate limiter + vm.startPrank(relayer); + uint256 rateLimitBudgetTracker = perTransfer; + + // for divisor amount of transfers, the rate limit should be enforced + for (uint256 i = 1; i < _divisor; i++) { + if (_amount / _divisor + rateLimitBudgetTracker < insuranceBalance / 4) { + nativeBridge.completeBridgeTransfer( + keccak256(abi.encodePacked(keccak256(abi.encodePacked(receiver)), receiver, _amount / _divisor, uint256(i + 1))), + keccak256(abi.encodePacked(receiver)), receiver, _amount / _divisor, i + 1); + rateLimitBudgetTracker += _amount / _divisor; + } else { + vm.expectRevert(INativeBridge.InboundRateLimitExceeded.selector); + nativeBridge.completeBridgeTransfer( + keccak256(abi.encodePacked(keccak256(abi.encodePacked(receiver)), receiver, _amount / _divisor, uint256(i + 1))), + keccak256(abi.encodePacked(receiver)), receiver, _amount / _divisor, i + 1); + } + } vm.stopPrank(); } @@ -74,8 +179,7 @@ contract NativeBridgeTest is Test { excludeSender(deployer); vm.assume(_recipient != address(0)); vm.assume(relayer != address(0)); - - _amount = bound(_amount, 1, 10000000000 * 10 ** 8); + _amount = bound(_amount, 1, moveToken.balanceOf(nativeBridge.insuranceFund()) / 4 - 2); // nonce cannot be uint256 max because we are testing a +1 addition case to the nonce _nonce = bound(_nonce, 1, type(uint256).max - 1); @@ -84,7 +188,11 @@ contract NativeBridgeTest is Test { moveToken.transfer(address(nativeBridge), _amount); console.log("Testing unathourized relayer"); - vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, address(this), keccak256("RELAYER_ROLE"))); + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, address(this), keccak256("RELAYER_ROLE") + ) + ); nativeBridge.completeBridgeTransfer(bridgeTransferId, _originator, _recipient, _amount, _nonce); vm.startPrank(relayer); @@ -111,10 +219,9 @@ contract NativeBridgeTest is Test { console.log("Testing correct values"); nativeBridge.completeBridgeTransfer(bridgeTransferId, _originator, _recipient, _amount, _nonce); - bytes32 _bridgeTransferId = - nativeBridge.noncesToIncomingBridgeTransferIds(_nonce); + uint256 nonce = nativeBridge.idsToInboundNonces(bridgeTransferId); - assertEq(_bridgeTransferId, bridgeTransferId); + assertEq(nonce, _nonce); vm.stopPrank(); } @@ -131,7 +238,7 @@ contract NativeBridgeTest is Test { originators[i] = keccak256(abi.encodePacked(i)); recipients[i] = address(uint160(i + 1)); amounts[i] = i + 1; - nonces[i] = i; + nonces[i] = i + 1; bridgeTransferIds[i] = keccak256(abi.encodePacked(originators[i], recipients[i], amounts[i], nonces[i])); fundContract += amounts[i]; } @@ -143,10 +250,9 @@ contract NativeBridgeTest is Test { nativeBridge.batchCompleteBridgeTransfer(bridgeTransferIds, originators, recipients, amounts, nonces); for (uint256 i; i < length; i++) { - bytes32 bridgeTransferId = - nativeBridge.noncesToIncomingBridgeTransferIds(nonces[i]); + uint256 nonce = nativeBridge.idsToInboundNonces(bridgeTransferIds[i]); - assertEq(bridgeTransferId, bridgeTransferIds[i]); + assertEq(nonce, nonces[i]); } vm.expectRevert(INativeBridge.CompletedBridgeTransferId.selector); diff --git a/protocol-units/bridge/indexer-db/Cargo.toml b/protocol-units/bridge/indexer-db/Cargo.toml index 38de0f50a..64b431228 100644 --- a/protocol-units/bridge/indexer-db/Cargo.toml +++ b/protocol-units/bridge/indexer-db/Cargo.toml @@ -15,12 +15,17 @@ diesel_migrations = { workspace = true } bigdecimal = { workspace = true } serde = { workspace = true } bridge-util = { workspace = true } +bridge-config = { workspace = true } anyhow = { workspace = true } hex = { workspace = true } chrono = { workspace = true } +tracing.workspace = true +godfig = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } [dev-dependencies] -tokio = { workspace = true } + [features] default = [] diff --git a/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/down.sql b/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/down.sql deleted file mode 100644 index 0727916e9..000000000 --- a/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/down.sql +++ /dev/null @@ -1,9 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE refunded_events; -DROP TABLE cancelled_events; -DROP TABLE counter_party_completed_events; -DROP TABLE initiator_completed_events; -DROP TABLE locked_events; -DROP TABLE initiated_events; -DROP TABLE wait_and_complete_initiators; -DROP TABLE lock_bridge_transfers; \ No newline at end of file diff --git a/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/up.sql b/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/up.sql deleted file mode 100644 index bb23e4f1b..000000000 --- a/protocol-units/bridge/indexer-db/migrations/2024-11-10-112030_indexed_bridge_actions/up.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE lock_bridge_transfers ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - hash_lock VARCHAR(64) NOT NULL, - initiator VARCHAR(64) NOT NULL, -- Address stored as bytes - recipient VARCHAR(64) NOT NULL, -- Address stored as bytes - amount NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE wait_and_complete_initiators ( - id SERIAL PRIMARY KEY, - wait_time_secs BIGINT NOT NULL, -- u64 field - pre_image VARCHAR(64) NOT NULL, -- Pre-image of the hash lock, stored as bytes - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE initiated_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - initiator VARCHAR(64) NOT NULL, - recipient VARCHAR(64) NOT NULL, - hash_lock VARCHAR(64) NOT NULL, - time_lock BIGINT NOT NULL, - amount NUMERIC NOT NULL, - state SMALLINT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE locked_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - initiator VARCHAR(64) NOT NULL, -- Initiator address as bytes - recipient VARCHAR(64) NOT NULL, -- Recipient address as bytes - hash_lock VARCHAR(64) NOT NULL, - time_lock BIGINT NOT NULL, - amount NUMERIC NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE initiator_completed_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE counter_party_completed_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - pre_image VARCHAR(64) NOT NULL, -- Pre-image of the hash lock - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE cancelled_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE refunded_events ( - id SERIAL PRIMARY KEY, - bridge_transfer_id VARCHAR(64) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); diff --git a/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/down.sql b/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/down.sql new file mode 100644 index 000000000..d9a1e3ce9 --- /dev/null +++ b/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` +DROP TABLE abort_replay_transfers; +DROP TABLE completed_remove_state; +DROP TABLE complete_bridge_transfers; +DROP TABLE completed_events; +DROP TABLE initiated_events; \ No newline at end of file diff --git a/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/up.sql b/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/up.sql new file mode 100644 index 000000000..843d43bac --- /dev/null +++ b/protocol-units/bridge/indexer-db/migrations/2024-12-10-112030_indexed_bridge_actions/up.sql @@ -0,0 +1,46 @@ +CREATE TABLE initiated_events ( + id SERIAL PRIMARY KEY, + bridge_transfer_id VARCHAR(64) NOT NULL, + initiator VARCHAR(64) NOT NULL, -- Address stored as bytes + recipient VARCHAR(64) NOT NULL, -- Address stored as bytes + amount NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues + nonce NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE completed_events ( + id SERIAL PRIMARY KEY, + bridge_transfer_id VARCHAR(64) NOT NULL, + initiator VARCHAR(64) NOT NULL, + recipient VARCHAR(64) NOT NULL, + amount NUMERIC NOT NULL, + nonce NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE complete_bridge_transfers ( + id SERIAL PRIMARY KEY, + bridge_transfer_id VARCHAR(64) NOT NULL, + initiator VARCHAR(64) NOT NULL, + recipient VARCHAR(64) NOT NULL, + amount NUMERIC NOT NULL, + nonce NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE completed_remove_state ( + id SERIAL PRIMARY KEY, + bridge_transfer_id VARCHAR(64) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE abort_replay_transfers ( + id SERIAL PRIMARY KEY, + bridge_transfer_id VARCHAR(64) NOT NULL, + initiator VARCHAR(64) NOT NULL, + recipient VARCHAR(64) NOT NULL, + amount NUMERIC NOT NULL, + nonce NUMERIC NOT NULL, -- Using NUMERIC to avoid floating-point issues + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + diff --git a/protocol-units/bridge/indexer-db/src/client.rs b/protocol-units/bridge/indexer-db/src/client.rs index d69086422..59e0ab8de 100644 --- a/protocol-units/bridge/indexer-db/src/client.rs +++ b/protocol-units/bridge/indexer-db/src/client.rs @@ -1,6 +1,7 @@ use crate::migrations::run_migrations; use crate::models::*; use crate::schema::*; +use bridge_config::Config; use bridge_util::chains::bridge_contracts::BridgeContractEvent; use bridge_util::types::BridgeTransferId; use bridge_util::TransferActionType; @@ -13,11 +14,7 @@ pub struct Client { pub struct BridgeEventPackage { pub initiated_events: Vec, - pub locked_events: Vec, - pub initiator_completed_events: Vec, - pub counter_party_completed_events: Vec, - pub cancelled_events: Vec, - pub refunded_events: Vec, + pub completed_events: Vec, } impl Client { @@ -26,10 +23,8 @@ impl Client { Self { conn } } - /// Gets the client from an environment variable containing the postgresql url. - pub fn from_env() -> Result { - let url = std::env::var("BRIDGE_INDEXER_DATABASE_URL")?; - let conn = PgConnection::establish(&url) + pub fn from_bridge_config(config: &Config) -> Result { + let conn = PgConnection::establish(&config.indexer.indexer_url) .map_err(|e| anyhow::anyhow!("Failed to connect to postgresql instance: {}", e))?; Ok(Self::new(conn)) } @@ -40,64 +35,6 @@ impl Client { Ok(()) } - /// Inserts a new transfer action into the database. - pub fn insert_transfer_action( - &mut self, - action_type: TransferActionType, - ) -> Result<(), diesel::result::Error> { - match action_type { - TransferActionType::LockBridgeTransfer { - bridge_transfer_id, - hash_lock, - initiator, - recipient, - amount, - } => { - diesel::insert_into(lock_bridge_transfers::table) - .values(NewLockBridgeTransfer { - bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), - hash_lock: hex::encode(hash_lock.0.to_vec()), - initiator: hex::encode(initiator.0.to_vec()), - recipient: hex::encode(recipient.0.to_vec()), - amount: amount.0.into(), - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - TransferActionType::WaitAndCompleteInitiator(wait_time_secs, hash_lock_pre_image) => { - diesel::insert_into(wait_and_complete_initiators::table) - .values(NewWaitAndCompleteInitiator { - wait_time_secs: wait_time_secs as i64, - pre_image: hex::encode(hash_lock_pre_image.0.to_vec()), - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - TransferActionType::RefundInitiator => { - // do nothing - } - TransferActionType::TransferDone => { - // do nothing - } - TransferActionType::NoAction => { - // do nothing - } - } - - Ok(()) - } - - /// Gets the lock bridge transfer action with a given bridge transfer id. - pub fn get_lock_bridge_transfer_action( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> Result { - let bridge_transfer_id = hex::encode(bridge_transfer_id.0.to_vec()); - lock_bridge_transfers::table - .filter(lock_bridge_transfers::bridge_transfer_id.eq(bridge_transfer_id)) - .first::(&mut self.conn) - } - /// Inserts a new bridge contract event into the database. pub fn insert_bridge_contract_event( &mut self, @@ -106,65 +43,28 @@ impl Client { where A: Into>, { + tracing::info!("Indexer insert_bridge_contract_event event:{contract_event}"); match contract_event { - BridgeContractEvent::Initiated(bridge_transfer_details) => { + BridgeContractEvent::Initiated(details) => { diesel::insert_into(initiated_events::table) .values(NewInitiatedEvent { - bridge_transfer_id: hex::encode( - bridge_transfer_details.bridge_transfer_id.0.to_vec(), - ), - initiator: hex::encode(bridge_transfer_details.initiator.0.into()), - recipient: hex::encode(bridge_transfer_details.recipient.0.to_vec()), - hash_lock: hex::encode(bridge_transfer_details.hash_lock.0.to_vec()), - time_lock: bridge_transfer_details.time_lock.0 as i64, - amount: bridge_transfer_details.amount.0.into(), - state: 0, - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - BridgeContractEvent::Locked(lock_details) => { - diesel::insert_into(locked_events::table) - .values(NewLockedEvent { - bridge_transfer_id: hex::encode(lock_details.bridge_transfer_id.0.to_vec()), - initiator: hex::encode::>(lock_details.initiator.0.into()), - recipient: hex::encode::>(lock_details.recipient.0.into()), - hash_lock: hex::encode(lock_details.hash_lock.0.to_vec()), - time_lock: lock_details.time_lock.0 as i64, - amount: lock_details.amount.0.into(), - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - BridgeContractEvent::InitiatorCompleted(initiator_completed_events) => { - diesel::insert_into(initiator_completed_events::table) - .values(NewInitiatorCompletedEvent { - bridge_transfer_id: hex::encode(initiator_completed_events.0.to_vec()), - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - BridgeContractEvent::CounterPartyCompleted(bridge_transfer_id, hash_lock_pre_image) => { - diesel::insert_into(counter_party_completed_events::table) - .values(NewCounterPartyCompletedEvent { - bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), - pre_image: hex::encode(hash_lock_pre_image.0.to_vec()), + bridge_transfer_id: hex::encode(details.bridge_transfer_id.0.to_vec()), + initiator: hex::encode(details.initiator.0.into()), + recipient: hex::encode(details.recipient.0.to_vec()), + amount: details.amount.0.into(), + nonce: details.nonce.0.into(), created_at: chrono::Utc::now().naive_utc(), }) .execute(&mut self.conn)?; } - BridgeContractEvent::Cancelled(bridge_transfer_id) => { - diesel::insert_into(cancelled_events::table) - .values(NewCancelledEvent { - bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), - created_at: chrono::Utc::now().naive_utc(), - }) - .execute(&mut self.conn)?; - } - BridgeContractEvent::Refunded(bridge_transfer_id) => { - diesel::insert_into(refunded_events::table) - .values(NewRefundedEvent { - bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), + BridgeContractEvent::Completed(details) => { + diesel::insert_into(completed_events::table) + .values(NewCompletedEvent { + bridge_transfer_id: hex::encode(details.bridge_transfer_id.0.to_vec()), + initiator: hex::encode::>(details.initiator.0.into()), + recipient: hex::encode::>(details.recipient.0.into()), + amount: details.amount.0.into(), + nonce: details.nonce.0.into(), created_at: chrono::Utc::now().naive_utc(), }) .execute(&mut self.conn)?; @@ -185,36 +85,70 @@ impl Client { .filter(initiated_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) .load::(&mut self.conn)?; - let locked_events = locked_events::table - .filter(locked_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) - .load::(&mut self.conn)?; - - let initiator_completed_events = initiator_completed_events::table - .filter(initiator_completed_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) - .load::(&mut self.conn)?; + let completed_events = completed_events::table + .filter(completed_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) + .load::(&mut self.conn)?; - let counter_party_completed_events = counter_party_completed_events::table - .filter( - counter_party_completed_events::bridge_transfer_id.eq(bridge_transfer_id.clone()), - ) - .load::(&mut self.conn)?; - - let cancelled_events = cancelled_events::table - .filter(cancelled_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) - .load::(&mut self.conn)?; + Ok(BridgeEventPackage { initiated_events, completed_events }) + } - let refunded_events = refunded_events::table - .filter(refunded_events::bridge_transfer_id.eq(bridge_transfer_id.clone())) - .load::(&mut self.conn)?; + /// Inserts a new transfer action into the database. + pub fn insert_transfer_action( + &mut self, + bridge_transfer_id: BridgeTransferId, + action_type: TransferActionType, + ) -> Result<(), diesel::result::Error> { + match action_type { + TransferActionType::CompleteBridgeTransfer { + bridge_transfer_id, + initiator, + recipient, + amount, + nonce, + } => { + diesel::insert_into(complete_bridge_transfers::table) + .values(CompleteBridgeTransferAction { + bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), + initiator: hex::encode(initiator.0.to_vec()), + recipient: hex::encode(recipient.0.to_vec()), + amount: amount.0.into(), + nonce: nonce.0.into(), + created_at: chrono::Utc::now().naive_utc(), + }) + .execute(&mut self.conn)?; + } + TransferActionType::CompletedRemoveState => { + diesel::insert_into(completed_remove_state::table) + .values(CompletedRemoveStateAction { + bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), + created_at: chrono::Utc::now().naive_utc(), + }) + .execute(&mut self.conn)?; + } + TransferActionType::AbortedReplay { + bridge_transfer_id, + initiator, + recipient, + amount, + nonce, + wait_time_sec, + } => { + diesel::insert_into(abort_replay_transfers::table) + .values(AbortReplayTransferAction { + bridge_transfer_id: hex::encode(bridge_transfer_id.0.to_vec()), + initiator: hex::encode(initiator.0.to_vec()), + recipient: hex::encode(recipient.0.to_vec()), + amount: amount.0.into(), + nonce: nonce.0.into(), + wait_time_sec: wait_time_sec.into(), + created_at: chrono::Utc::now().naive_utc(), + }) + .execute(&mut self.conn)?; + } + TransferActionType::NoAction => (), + } - Ok(BridgeEventPackage { - initiated_events, - locked_events, - initiator_completed_events, - counter_party_completed_events, - cancelled_events, - refunded_events, - }) + Ok(()) } } diff --git a/protocol-units/bridge/indexer-db/src/lib.rs b/protocol-units/bridge/indexer-db/src/lib.rs index 53abf6177..1f9655cc2 100644 --- a/protocol-units/bridge/indexer-db/src/lib.rs +++ b/protocol-units/bridge/indexer-db/src/lib.rs @@ -1,4 +1,62 @@ +use crate::client::Client; +use bridge_config::Config; +use bridge_util::chains::bridge_contracts::BridgeContractMonitoring; +use bridge_util::types::BridgeTransferId; +use bridge_util::TransferActionType; +use tokio::select; +use tokio::sync::mpsc; +use tokio_stream::StreamExt; + pub mod client; pub mod migrations; pub mod models; pub mod schema; + +pub async fn run_indexer_client< + SOURCE: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, + TARGET: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, +>( + config: Config, + mut stream_source: impl BridgeContractMonitoring
, + mut stream_target: impl BridgeContractMonitoring
, + relayer_actions: Option>, +) -> Result<(), anyhow::Error> +where + Vec: From, + Vec: From, +{ + let mut indexer_db_client = match Client::from_bridge_config(&config) { + Ok(mut client) => { + client.run_migrations()?; + client + } + Err(e) => { + panic!("Failed to create indexer db client: {e:?}"); + } + }; + + loop { + select! { + // Wait on chain source events. + Some(event_res) = stream_source.next() =>{ + if let Err(err) = event_res.map_err(|err| err.to_string()).and_then(|event| { + indexer_db_client + .insert_bridge_contract_event(event) + .map_err(|err| err.to_string()) + }) { + tracing::error!("Indexer: Source event integration return an error:{err}") + } + } + // Wait on chain target events. + Some(event_res) = stream_target.next() =>{ + if let Err(err) = event_res.map_err(|err| err.to_string()).and_then(|event| { + indexer_db_client + .insert_bridge_contract_event(event) + .map_err(|err| err.to_string()) + }) { + tracing::error!("Indexer: Target event integration return an error:{err}") + } + } + } + } +} diff --git a/protocol-units/bridge/indexer-db/src/models.rs b/protocol-units/bridge/indexer-db/src/models.rs index e7914be9a..1caff0542 100644 --- a/protocol-units/bridge/indexer-db/src/models.rs +++ b/protocol-units/bridge/indexer-db/src/models.rs @@ -2,48 +2,6 @@ use crate::schema::*; use bigdecimal::BigDecimal; use diesel::prelude::*; -// LockBridgeTransfer mapping -#[derive(Debug, Insertable, Default)] -#[diesel(table_name = lock_bridge_transfers)] -pub struct NewLockBridgeTransfer { - pub bridge_transfer_id: String, - pub hash_lock: String, - pub initiator: String, - pub recipient: String, - pub amount: BigDecimal, - pub created_at: chrono::NaiveDateTime, -} - -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = lock_bridge_transfers)] -pub struct LockBridgeTransfer { - pub id: i32, - pub bridge_transfer_id: String, - pub hash_lock: String, - pub initiator: String, - pub recipient: String, - pub amount: BigDecimal, - pub created_at: chrono::NaiveDateTime, -} - -// WaitAndCompleteInitiator mapping -#[derive(Debug, Insertable, Default)] -#[diesel(table_name = wait_and_complete_initiators)] -pub struct NewWaitAndCompleteInitiator { - pub wait_time_secs: i64, - pub pre_image: String, - pub created_at: chrono::NaiveDateTime, -} - -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = wait_and_complete_initiators)] -pub struct WaitAndCompleteInitiator { - pub id: i32, - pub wait_time_secs: i64, - pub pre_image: String, - pub created_at: chrono::NaiveDateTime, -} - // InitiatedEvent mapping #[derive(Debug, Insertable, Default)] #[diesel(table_name = initiated_events)] @@ -51,10 +9,8 @@ pub struct NewInitiatedEvent { pub bridge_transfer_id: String, pub initiator: String, pub recipient: String, - pub hash_lock: String, - pub time_lock: i64, pub amount: BigDecimal, - pub state: i16, + pub nonce: BigDecimal, pub created_at: chrono::NaiveDateTime, } @@ -65,101 +21,61 @@ pub struct InitiatedEvent { pub bridge_transfer_id: String, pub initiator: String, pub recipient: String, - pub hash_lock: String, - pub time_lock: i64, pub amount: BigDecimal, - pub state: i16, + pub nonce: BigDecimal, pub created_at: chrono::NaiveDateTime, } // LockedEvent mapping #[derive(Debug, Insertable, Default)] -#[diesel(table_name = locked_events)] -pub struct NewLockedEvent { +#[diesel(table_name = completed_events)] +pub struct NewCompletedEvent { pub bridge_transfer_id: String, pub initiator: String, pub recipient: String, - pub hash_lock: String, - pub time_lock: i64, pub amount: BigDecimal, + pub nonce: BigDecimal, pub created_at: chrono::NaiveDateTime, } #[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = locked_events)] -pub struct LockedEvent { +#[diesel(table_name = completed_events)] +pub struct CompletedEvent { pub id: i32, pub bridge_transfer_id: String, pub initiator: String, pub recipient: String, - pub hash_lock: String, - pub time_lock: i64, pub amount: BigDecimal, + pub nonce: BigDecimal, pub created_at: chrono::NaiveDateTime, } -// InitiatorCompletedEvent mapping #[derive(Debug, Insertable, Default)] -#[diesel(table_name = initiator_completed_events)] -pub struct NewInitiatorCompletedEvent { +#[diesel(table_name = complete_bridge_transfers)] +pub struct CompleteBridgeTransferAction { pub bridge_transfer_id: String, + pub initiator: String, + pub recipient: String, + pub amount: BigDecimal, + pub nonce: BigDecimal, pub created_at: chrono::NaiveDateTime, } -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = initiator_completed_events)] -pub struct InitiatorCompletedEvent { - pub id: i32, - pub bridge_transfer_id: String, - pub created_at: chrono::NaiveDateTime, -} - -// CounterPartCompletedEvent mapping -#[derive(Debug, Insertable, Default)] -#[diesel(table_name = counter_party_completed_events)] -pub struct NewCounterPartyCompletedEvent { - pub bridge_transfer_id: String, - pub pre_image: String, - pub created_at: chrono::NaiveDateTime, -} - -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = counter_party_completed_events)] -pub struct CounterPartyCompletedEvent { - pub id: i32, - pub bridge_transfer_id: String, - pub pre_image: String, - pub created_at: chrono::NaiveDateTime, -} - -// CancelledEvent mapping #[derive(Debug, Insertable, Default)] -#[diesel(table_name = cancelled_events)] -pub struct NewCancelledEvent { +#[diesel(table_name = completed_remove_state)] +pub struct CompletedRemoveStateAction { pub bridge_transfer_id: String, pub created_at: chrono::NaiveDateTime, } -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = cancelled_events)] -pub struct CancelledEvent { - pub id: i32, - pub bridge_transfer_id: String, - pub created_at: chrono::NaiveDateTime, -} - -// RefundedEvent mapping #[derive(Debug, Insertable, Default)] -#[diesel(table_name = refunded_events)] -pub struct NewRefundedEvent { - pub bridge_transfer_id: String, - pub created_at: chrono::NaiveDateTime, -} - -#[derive(Debug, Queryable, Insertable)] -#[diesel(table_name = refunded_events)] -pub struct RefundedEvent { - pub id: i32, +#[diesel(table_name = abort_replay_transfers)] +pub struct AbortReplayTransferAction { pub bridge_transfer_id: String, + pub initiator: String, + pub recipient: String, + pub amount: BigDecimal, + pub nonce: BigDecimal, + pub wait_time_sec: BigDecimal, pub created_at: chrono::NaiveDateTime, } diff --git a/protocol-units/bridge/indexer-db/src/schema.rs b/protocol-units/bridge/indexer-db/src/schema.rs index 76d331c70..fec9cfdc8 100644 --- a/protocol-units/bridge/indexer-db/src/schema.rs +++ b/protocol-units/bridge/indexer-db/src/schema.rs @@ -1,55 +1,43 @@ use diesel::prelude::*; table! { - lock_bridge_transfers (id) { + initiated_events (id) { id -> Int4, bridge_transfer_id -> Text, - hash_lock -> Text, initiator -> Text, recipient -> Text, amount -> Numeric, + nonce -> Numeric, created_at -> Timestamp, } } table! { - wait_and_complete_initiators (id) { - id -> Int4, - wait_time_secs -> BigInt, - pre_image -> Text, - created_at -> Timestamp, - } -} - -table! { - initiated_events (id) { + completed_events (id) { id -> Int4, bridge_transfer_id -> Text, initiator -> Text, recipient -> Text, - hash_lock -> Text, - time_lock -> BigInt, amount -> Numeric, - state -> Int2, + nonce -> Numeric, created_at -> Timestamp, } } table! { - locked_events (id) { + complete_bridge_transfers (id) { id -> Int4, bridge_transfer_id -> Text, initiator -> Text, recipient -> Text, - hash_lock -> Text, - time_lock -> BigInt, amount -> Numeric, + nonce -> Numeric, created_at -> Timestamp, } } table! { - initiator_completed_events (id) { + completed_remove_state (id) { id -> Int4, bridge_transfer_id -> Text, created_at -> Timestamp, @@ -57,26 +45,14 @@ table! { } table! { - counter_party_completed_events (id) { - id -> Int4, - bridge_transfer_id -> Text, - pre_image -> Text, - created_at -> Timestamp, - } -} - -table! { - cancelled_events (id) { - id -> Int4, - bridge_transfer_id -> Text, - created_at -> Timestamp, - } -} - -table! { - refunded_events (id) { + abort_replay_transfers (id) { id -> Int4, bridge_transfer_id -> Text, + initiator -> Text, + recipient -> Text, + amount -> Numeric, + nonce -> Numeric, + wait_time_sec -> Numeric, created_at -> Timestamp, } } diff --git a/protocol-units/bridge/integration-tests/Cargo.toml b/protocol-units/bridge/integration-tests/Cargo.toml index 74a376211..e1d174e6c 100644 --- a/protocol-units/bridge/integration-tests/Cargo.toml +++ b/protocol-units/bridge/integration-tests/Cargo.toml @@ -12,6 +12,7 @@ version = { workspace = true } path = "src/lib.rs" [dependencies] +alloy-primitives = { workspace = true } aptos-framework = { workspace = true } aptos-language-e2e-tests = { workspace = true } aptos-logger = { workspace = true } @@ -26,11 +27,13 @@ reqwest = { workspace = true } alloy-network = { workspace = true } alloy-sol-types = { workspace = true } alloy-contract = { workspace = true } +ethabi = { workspace = true } poem = { workspace = true, features = ["test"] } rand = { workspace = true } serde_json = { workspace = true } url = { workspace = true } bridge-service = { workspace = true } +bridge-util = { workspace = true } bridge-setup = { workspace = true } bridge-config = { workspace = true } tokio = { workspace = true } @@ -38,11 +41,14 @@ tonic = { workspace = true } futures = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } +tiny-keccak = { workspace = true } + dot-movement = { workspace = true } godfig = { workspace = true } bridge-grpc = { workspace = true, features = ["server", "client"] } + [dev-dependencies] tokio = { workspace = true } tokio-stream = "0.1.16" diff --git a/protocol-units/bridge/integration-tests/src/lib.rs b/protocol-units/bridge/integration-tests/src/lib.rs index fa828a63d..588cb881b 100644 --- a/protocol-units/bridge/integration-tests/src/lib.rs +++ b/protocol-units/bridge/integration-tests/src/lib.rs @@ -1,133 +1,37 @@ -use alloy::primitives::{FixedBytes, Uint, U256}; -use alloy::{ - primitives::{keccak256, Address}, - providers::ProviderBuilder, - signers::local::PrivateKeySigner, -}; +use alloy::primitives::U256; +use alloy::{primitives::Address, providers::ProviderBuilder, signers::local::PrivateKeySigner}; use alloy_network::EthereumWallet; +use aptos_sdk::coin_client::CoinClient; +use aptos_sdk::move_types::identifier::Identifier; +use aptos_sdk::rest_client::aptos_api_types::{self, EntryFunctionId, MoveModuleId, ViewRequest}; use aptos_sdk::{ - rest_client::{aptos_api_types::Transaction as AptosTransaction, Client, FaucetClient}, + rest_client::{Client, FaucetClient, Response}, types::{account_address::AccountAddress, LocalAccount}, }; use bridge_config::Config; use bridge_service::chains::ethereum::types::MockMOVEToken; -use bridge_service::chains::ethereum::types::{ - AtomicBridgeCounterpartyMOVE, AtomicBridgeInitiatorMOVE, -}; use bridge_service::chains::ethereum::utils::send_transaction; use bridge_service::chains::ethereum::utils::send_transaction_rules; +use bridge_service::chains::movement::client_framework::FRAMEWORK_ADDRESS; +use bridge_service::chains::{ + ethereum::{client::EthClient, types::AlloyProvider}, + movement::{client_framework::MovementClientFramework, utils::MovementAddress}, +}; use bridge_service::types::Amount; use bridge_service::types::BridgeAddress; -use bridge_service::types::HashLock; -use bridge_service::{ - chains::{ - bridge_contracts::{BridgeContractError, BridgeContractResult}, - ethereum::{ - client::EthClient, - types::{AlloyProvider, EthAddress, EthHash}, - }, - movement::{ - client_framework::{MovementClientFramework, FRAMEWORK_ADDRESS}, - utils::{self as movement_utils, MovementAddress, MovementHash}, - }, - }, - types::{BridgeTransferId, HashLockPreImage}, -}; +use bridge_service::types::BridgeTransferId; +use bridge_service::types::Nonce; +use bridge_util::chains::bridge_contracts::BridgeClientContract; +use ethabi; use godfig::{backend::config_file::ConfigFile, Godfig}; -use rand::{distributions::Alphanumeric, thread_rng, Rng, SeedableRng}; +use rand::SeedableRng; use std::{ - convert::TryInto, str::FromStr, sync::{Arc, RwLock}, }; +use tiny_keccak::{Hasher, Keccak}; use url::Url; -pub mod utils; - -#[derive(Clone)] -pub struct EthToMovementCallArgs { - pub initiator: Vec, - pub recipient: MovementAddress, - pub bridge_transfer_id: MovementHash, - pub hash_lock: MovementHash, - pub time_lock: u64, - pub amount: u64, -} - -#[derive(Clone)] -pub struct MovementToEthCallArgs { - pub initiator: MovementAddress, - pub recipient: Vec, - pub bridge_transfer_id: EthHash, - pub hash_lock: EthHash, - pub time_lock: u64, - pub amount: u64, - pub pre_image: [u8; 32], -} - -impl Default for EthToMovementCallArgs { - fn default() -> Self { - // Generate 6 random alphanumeric characters - let random_suffix: String = - thread_rng().sample_iter(&Alphanumeric).take(6).map(char::from).collect(); - - // Construct the bridge_transfer_id with the random suffix - let mut bridge_transfer_id = b"00000000000000000000000tra".to_vec(); - bridge_transfer_id.extend_from_slice(random_suffix.as_bytes()); - - Self { - // Dummy valid EIP-55 address used in framework modules - // initiator: b"32Be343B94f860124dC4fEe278FDCBD38C102D88".to_vec(), - // Actual Eth address - initiator: b"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc".to_vec(), - // All lowercase version: - //initiator: b"0x32be343b94f860124dc4fee278fdcbd38c102d88".to_vec(), - // Dummy recipient address - recipient: MovementAddress(AccountAddress::new(*b"0x00000000000000000000000000face")), - // Convert to [u8; 32] with explicit type annotation - bridge_transfer_id: MovementHash( - bridge_transfer_id - .as_slice() - .try_into() - .expect("Expected bridge_transfer_id to be 32 bytes"), - ), - hash_lock: MovementHash(*keccak256(b"secret")), - time_lock: 3600, - amount: 100, - } - } -} - -impl Default for MovementToEthCallArgs { - fn default() -> Self { - // Generate a 6-character random alphanumeric suffix - let random_suffix: String = - thread_rng().sample_iter(&Alphanumeric).take(6).map(char::from).collect(); - - // Construct the bridge_transfer_id with the random suffix - let mut bridge_transfer_id = b"00000000000000000000000tra".to_vec(); - bridge_transfer_id.extend_from_slice(random_suffix.as_bytes()); - - // Generate a random 32-byte secret - let pre_image: [u8; 32] = thread_rng().gen(); - - Self { - initiator: MovementAddress(AccountAddress::new(*b"0x000000000000000000000000A55018")), - recipient: b"32Be343B94f860124dC4fEe278FDCBD38C102D88".to_vec(), - bridge_transfer_id: EthHash( - bridge_transfer_id - .as_slice() - .try_into() - .expect("Expected bridge_transfer_id to be 32 bytes"), - ), - hash_lock: EthHash(*keccak256(&pre_image)), // Hash the secret for the hash lock - time_lock: 3600, - amount: 100, - pre_image, // Store the generated secret in the struct - } - } -} - pub struct HarnessEthClient { pub eth_rpc_url: String, pub signer_private_key: PrivateKeySigner, @@ -144,7 +48,9 @@ impl HarnessEthClient { .parse::() .expect("Error during parsing signer private key?"); - let eth_client = EthClient::new(&config.eth).await.expect("Failed to create EthClient"); + let eth_client = EthClient::build_with_config(&config.eth) + .await + .expect("Failed to create EthClient"); HarnessEthClient { eth_client, eth_rpc_url, signer_private_key } } @@ -170,7 +76,7 @@ impl HarnessEthClient { signer_private_key } - pub fn get_initiator(config: &Config) -> Address { + pub fn get_initiator_address(config: &Config) -> Address { HarnessEthClient::get_initiator_private_key(config).address() } @@ -182,16 +88,34 @@ impl HarnessEthClient { signer_private_key } - pub fn get_recipeint_address(config: &Config) -> Address { + pub fn get_recipient_address(config: &Config) -> Address { HarnessEthClient::get_recipient_private_key(config).address() } + pub fn calculate_bridge_transfer_id( + initiator: AccountAddress, + recipient: Address, + amount: Amount, + nonce: Nonce, + ) -> BridgeTransferId { + let mut hasher = Keccak::v256(); + hasher.update(&initiator.as_slice()); + hasher.update(&recipient.as_slice()); + let encoded = ethabi::encode(&[ethabi::Token::Uint(ethabi::Uint::from(amount.0 as u128))]); + hasher.update(&encoded); + let encoded = ethabi::encode(&[ethabi::Token::Uint(ethabi::Uint::from(nonce.0))]); + hasher.update(&encoded); + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + + BridgeTransferId(output) + } + pub async fn initiate_eth_bridge_transfer( &self, config: &Config, initiator_privatekey: PrivateKeySigner, recipient: MovementAddress, - hash_lock: HashLock, amount: Amount, ) -> Result<(), anyhow::Error> { let initiator_address = initiator_privatekey.address(); @@ -239,20 +163,22 @@ impl HarnessEthClient { .await?; } - let initiator_rpc_provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(EthereumWallet::from(initiator_privatekey)) - .on_builtin(&config.eth.eth_rpc_connection_url()) - .await?; + // let initiator_rpc_provider = ProviderBuilder::new() + // .with_recommended_fillers() + // .wallet(EthereumWallet::from(initiator_privatekey)) + // .on_builtin(&config.eth.eth_rpc_connection_url()) + // .await?; + let mut initiator_client = + EthClient::build_with_signer(initiator_privatekey, &config.eth).await?; let mock_move_token = MockMOVEToken::new( Address::from_str(&config.eth.eth_move_token_contract)?, - &initiator_rpc_provider, + &initiator_client.rpc_provider, ); // Approve the ETH initiator contract to spend Amount of MOVE let approve_call = mock_move_token - .approve(Address::from_str(&config.eth.eth_initiator_contract)?, move_value) + .approve(Address::from_str(&config.eth.eth_native_contract)?, move_value) .from(initiator_address); send_transaction( @@ -264,30 +190,9 @@ impl HarnessEthClient { ) .await?; - // Instantiate AtomicBridgeInitiatorMOVE - let initiator_contract_address = config.eth.eth_initiator_contract.parse()?; - let initiator_contract = - AtomicBridgeInitiatorMOVE::new(initiator_contract_address, &initiator_rpc_provider); - + //Initiate transfer let recipient_address = BridgeAddress(Into::>::into(recipient)); - let recipient_bytes: [u8; 32] = - recipient_address.0.try_into().expect("Recipient address must be 32 bytes"); - - let call = initiator_contract - .initiateBridgeTransfer( - U256::from(amount.0), - FixedBytes(recipient_bytes), - FixedBytes(hash_lock.0), - ) - .from(initiator_address); - let _ = send_transaction( - call, - initiator_address, - &send_transaction_rules(), - config.eth.transaction_send_retries, - config.eth.gas_limit as u128, - ) - .await?; + initiator_client.initiate_bridge_transfer(recipient_address, amount).await?; Ok(()) } @@ -313,8 +218,12 @@ impl HarnessMvtClient { LocalAccount::generate(&mut rng) } + pub fn signer_address(&self) -> AccountAddress { + self.movement_client.signer().address() + } + pub async fn build(config: &Config) -> Self { - let movement_client = MovementClientFramework::new(&config.movement) + let movement_client = MovementClientFramework::build_with_config(&config.movement) .await .expect("Failed to create MovementClient"); @@ -332,6 +241,99 @@ impl HarnessMvtClient { HarnessMvtClient { movement_client, rest_client, faucet_client } } + fn normalize_to_32_bytes(value: u64) -> Vec { + // Convert the u64 value to a u256 (as bytes) + let bytes = ethabi::encode(&[ethabi::Token::Uint(ethabi::Uint::from(value as u128))]); + + bytes + } + + pub async fn get_bridge_fee(&self) -> Result { + // Create the view request to call the bridge_fee Move function + let view_request = ViewRequest { + function: EntryFunctionId { + module: MoveModuleId { + address: FRAMEWORK_ADDRESS.clone().into(), + name: aptos_api_types::IdentifierWrapper( + Identifier::new("native_bridge").map_err(|_| { + anyhow::anyhow!("Failed to create module name identifier") + })?, + ), + }, + name: aptos_api_types::IdentifierWrapper( + Identifier::new("bridge_fee").map_err(|_| { + anyhow::anyhow!("Failed to create function name identifier") + })?, + ), + }, + type_arguments: vec![], + arguments: vec![], + }; + + // Make the view call + let response: Response> = self + .rest_client + .view(&view_request, None) + .await + .map_err(|err| anyhow::anyhow!("Failed to call view function: {:?}", err))?; + + let values = response.inner(); + + tracing::info!("Raw response: {:?}", values); + + // Ensure the response contains exactly one value + if values.len() != 1 { + return Err(anyhow::anyhow!("Unexpected response length: {}", values.len())); + } + + // Parse the bridge fee from the string + let fee_str = values[0] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Bridge fee is not a string"))?; + let fee = fee_str + .parse::() + .map_err(|err| anyhow::anyhow!("Failed to parse bridge fee as u64: {:?}", err))?; + + Ok(fee) + } + + pub fn calculate_bridge_transfer_id( + initiator: Address, + recipient: AccountAddress, + amount: Amount, + nonce: Nonce, + ) -> BridgeTransferId { + let mut hasher = Keccak::v256(); + hasher.update(&initiator.as_slice()); + hasher.update(&bcs::to_bytes(&recipient).unwrap()); + let encoded_amount = Self::normalize_to_32_bytes(amount.0); + hasher.update(&encoded_amount); + let encoded_nonce = Self::normalize_to_32_bytes(nonce.0 as u64); + hasher.update(&encoded_nonce); + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + + BridgeTransferId(output) + } + + pub async fn initiate_bridge_transfer_helper_framework( + config: &Config, + initiator_privatekey: LocalAccount, + recipient: Vec, + amount: u64, + ) -> Result<(), anyhow::Error> { + //Create a client with Initiator as signer. + let mut movement_client = + MovementClientFramework::build_with_signer(initiator_privatekey, &config.movement) + .await?; + + movement_client + .initiate_bridge_transfer(BridgeAddress(recipient), Amount(amount)) + .await?; + + Ok(()) + } + pub async fn fund_account(&self) -> LocalAccount { let account = LocalAccount::generate(&mut rand::rngs::OsRng); self.faucet_client @@ -343,77 +345,26 @@ impl HarnessMvtClient { account } - pub async fn init_set_timelock(&mut self, timelock: u64) -> Result<(), BridgeContractError> { - self.movement_client.initiator_set_timelock(timelock).await?; - Ok(()) - } - - pub async fn initiate_bridge_transfer( + pub async fn fund_signer_and_check_balance_framework( &mut self, - initiator: &LocalAccount, - recipient: EthAddress, - hash_lock: HashLock, - amount: u64, - ) -> BridgeContractResult<()> { - let recipient_bytes: Vec = recipient.into(); - let args = vec![ - movement_utils::serialize_vec_initiator(&recipient_bytes)?, - movement_utils::serialize_vec_initiator(&hash_lock.0[..])?, - movement_utils::serialize_u64_initiator(&amount)?, - ]; - - let payload = movement_utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - "atomic_bridge_initiator", - "initiate_bridge_transfer", - Vec::new(), - args, - ); - - let _ = movement_utils::send_and_confirm_aptos_transaction( - &self.movement_client.rest_client, - initiator, - payload, - ) - .await - .map_err(|_| BridgeContractError::InitiateTransferError)?; - - Ok(()) - } + expected_balance: u64, + ) -> Result<(), anyhow::Error> { + let coin_client = CoinClient::new(&self.rest_client); + self.faucet_client + .write() + .unwrap() + .fund(self.signer_address(), expected_balance) + .await?; - pub async fn counterparty_complete_bridge_transfer( - &mut self, - recipient_privatekey: LocalAccount, - bridge_transfer_id: BridgeTransferId, - preimage: HashLockPreImage, - ) -> BridgeContractResult { - let unpadded_preimage = { - let mut end = preimage.0.len(); - while end > 0 && preimage.0[end - 1] == 0 { - end -= 1; - } - &preimage.0[..end] - }; - let args2 = vec![ - bridge_service::chains::movement::utils::serialize_vec(&bridge_transfer_id.0[..])?, - bridge_service::chains::movement::utils::serialize_vec(&unpadded_preimage)?, - ]; - - let payload = bridge_service::chains::movement::utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - bridge_service::chains::movement::client_framework::COUNTERPARTY_MODULE_NAME, - "complete_bridge_transfer", - Vec::new(), - args2, + let balance = coin_client.get_account_balance(&self.signer_address()).await?; + assert!( + balance >= expected_balance, + "Expected Movement Client to have at least {}, but found {}", + expected_balance, + balance ); - bridge_service::chains::movement::utils::send_and_confirm_aptos_transaction( - &self.rest_client, - &recipient_privatekey, - payload, - ) - .await - .map_err(|_| BridgeContractError::CompleteTransferError) + Ok(()) } } @@ -453,4 +404,13 @@ impl TestHarness { let test_harness = HarnessEthClient::build(&config).await; Ok((test_harness, config)) } + + // Get a different nonce for every test + pub fn create_nonce() -> Nonce { + let start = std::time::SystemTime::now(); + let duration_since_epoch = + start.duration_since(std::time::UNIX_EPOCH).expect("Time went backwards"); + let timestamp_seconds = duration_since_epoch.as_millis(); + Nonce(timestamp_seconds) + } } diff --git a/protocol-units/bridge/integration-tests/src/utils.rs b/protocol-units/bridge/integration-tests/src/utils.rs deleted file mode 100644 index 6902bd7d0..000000000 --- a/protocol-units/bridge/integration-tests/src/utils.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::HarnessMvtClient; -use alloy::hex; -use anyhow::Result; -use aptos_sdk::{ - coin_client::CoinClient, rest_client::Transaction, types::account_address::AccountAddress, -}; -use bridge_service::chains::bridge_contracts::{BridgeContract, BridgeContractError}; -use bridge_service::chains::movement::client_framework::MovementClientFramework; -use bridge_service::chains::movement::utils::{ - self as movement_utils, MovementAddress, MovementHash, -}; -use bridge_service::types::{Amount, BridgeAddress, BridgeTransferDetails, HashLock}; -use serde_json::Value; -use tracing::debug; - -const FRAMEWORK_ADDRESS: AccountAddress = AccountAddress::new([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -]); - -pub fn assert_bridge_transfer_details( - details: &BridgeTransferDetails, // MovementAddress for initiator - expected_bridge_transfer_id: [u8; 32], - expected_hash_lock: [u8; 32], - expected_sender_address: AccountAddress, - expected_recipient: Vec, - expected_amount: u64, - expected_state: u8, -) { - assert_eq!(details.bridge_transfer_id.0, expected_bridge_transfer_id); - assert_eq!(details.hash_lock.0, expected_hash_lock); - assert_eq!(details.initiator.0 .0, expected_sender_address); - assert_eq!(details.recipient.0, expected_recipient); - assert_eq!(details.amount.0, expected_amount); - assert_eq!(details.state, expected_state, "Bridge transfer state mismatch."); -} - -pub fn assert_counterparty_bridge_transfer_details_framework( - details: &BridgeTransferDetails, - expected_sender_address: String, - expected_recipient: Vec, - expected_amount: u64, - expected_hash_lock: [u8; 32], - expected_time_lock: u64, -) { - assert_eq!(details.initiator.to_string(), expected_sender_address); - assert_eq!(details.recipient, BridgeAddress(expected_recipient)); - assert_eq!(details.amount, Amount(expected_amount)); - assert_eq!(details.hash_lock.0, expected_hash_lock); - assert_eq!(details.time_lock.0, expected_time_lock); -} - -pub async fn fetch_bridge_transfer_details( - movement_client: &mut MovementClientFramework, - bridge_transfer_id: Vec, -) -> Result, anyhow::Error> { - let rest_client = movement_client.rest_client(); - let account_address = FRAMEWORK_ADDRESS; - let resource_tag = "0x1::atomic_bridge_store::SmartTableWrapper, 0x1::atomic_bridge_store::BridgeTransferDetails>"; - - let resource_response = - rest_client - .get_account_resource(account_address, resource_tag) - .await - .map_err(|e| anyhow::Error::msg(format!("Failed to fetch resource: {:?}", e)))?; - - let json_value: Value = resource_response.into_inner().unwrap().data; - - if let Some(transfers) = json_value.get("inner").and_then(|t| t.get("buckets")) { - for (key, value) in transfers.as_object().unwrap().iter() { - // Convert the key into Vec - let key_vec = hex::decode(key).expect("Failed to decode key"); - - if key_vec == bridge_transfer_id { - let bridge_transfer_details: BridgeTransferDetails = - serde_json::from_value(value.clone()).map_err(|e| { - anyhow::Error::msg(format!( - "Failed to deserialize BridgeTransferDetails: {:?}", - e - )) - })?; - return Ok(bridge_transfer_details); - } - } - } - - Err(anyhow::Error::msg("No matching bridge transfer details found")) -} - -pub async fn fund_and_check_balance( - movement_harness: &mut HarnessMvtClient, - expected_balance: u64, -) -> Result<()> { - let movement_client_signer = movement_harness.movement_client.signer(); - let rest_client = movement_harness.rest_client.clone(); - let coin_client = CoinClient::new(&rest_client); - let faucet_client = movement_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer.address(), expected_balance).await?; - faucet_client - .fund(AccountAddress::from_hex_literal("0xface")?, expected_balance) - .await?; - - let balance = coin_client.get_account_balance(&movement_client_signer.address()).await?; - assert!( - balance >= expected_balance, - "Expected Movement Client to have at least {}, but found {}", - expected_balance, - balance - ); - - Ok(()) -} - -pub async fn fund_and_check_balance_framework( - movement_harness: &mut HarnessMvtClient, - expected_balance: u64, -) -> Result<()> { - let movement_client_signer = movement_harness.movement_client.signer(); - let rest_client = movement_harness.rest_client.clone(); - let coin_client = CoinClient::new(&rest_client); - let faucet_client = movement_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer.address(), expected_balance).await?; - - let balance = coin_client.get_account_balance(&movement_client_signer.address()).await?; - assert!( - balance >= expected_balance, - "Expected Movement Client to have at least {}, but found {}", - expected_balance, - balance - ); - - Ok(()) -} - -pub async fn initiate_bridge_transfer_helper( - movement_client: &mut MovementClientFramework, - initiator: AccountAddress, - recipient: Vec, - hash_lock: [u8; 32], - amount: u64, - timelock_modify: bool, -) -> Result<(), BridgeContractError> { - if timelock_modify { - // Set the timelock to 1 second for testing - movement_client.initiator_set_timelock(1).await.expect("Failed to set timelock"); - } - - // Mint MovETH to the initiator's address - let mint_amount = 200 * 100_000_000; // Assuming 8 decimals for MovETH - - let mint_args = vec![ - movement_utils::serialize_address_initiator(&movement_client.signer().address())?, // Mint to initiator's address - movement_utils::serialize_u64_initiator(&mint_amount)?, // Amount to mint (200 MovETH) - ]; - - let mint_payload = movement_utils::make_aptos_payload( - movement_client.native_address, // Address where moveth module is published - "moveth", - "mint", - Vec::new(), - mint_args, - ); - - // Send transaction to mint MovETH - movement_utils::send_and_confirm_aptos_transaction( - &movement_client.rest_client(), - movement_client.signer(), - mint_payload, - ) - .await - .map_err(|_| BridgeContractError::MintError)?; - - debug!("Successfully minted 200 MovETH to the initiator"); - - // Initiate the bridge transfer - movement_client - .initiate_bridge_transfer( - BridgeAddress(MovementAddress(initiator)), - BridgeAddress(recipient), - HashLock(MovementHash(hash_lock).0), - Amount(amount), - ) - .await - .expect("Failed to initiate bridge transfer"); - - Ok(()) -} - -pub async fn initiate_bridge_transfer_helper_framework( - movement_client: &mut MovementClientFramework, - initiator: AccountAddress, - recipient: Vec, - hash_lock: [u8; 32], - amount: u64, -) -> Result<(), BridgeContractError> { - movement_client - .initiate_bridge_transfer( - BridgeAddress(MovementAddress(initiator)), - BridgeAddress(recipient), - HashLock(MovementHash(hash_lock).0), - Amount(amount), - ) - .await - .expect("Failed to initiate bridge transfer"); - - Ok(()) -} diff --git a/protocol-units/bridge/integration-tests/tests/bridge_e2e_test.rs b/protocol-units/bridge/integration-tests/tests/bridge_e2e_test.rs deleted file mode 100644 index 893e62ba1..000000000 --- a/protocol-units/bridge/integration-tests/tests/bridge_e2e_test.rs +++ /dev/null @@ -1,259 +0,0 @@ -use alloy::primitives::{keccak256, Address}; -use alloy::primitives::{FixedBytes, U256}; -use alloy::providers::ProviderBuilder; -use alloy::signers::local::PrivateKeySigner; -use alloy_network::EthereumWallet; -use anyhow::Result; -use aptos_types::account_address::AccountAddress; -use bridge_config::Config; -use bridge_integration_tests::HarnessEthClient; -use bridge_integration_tests::TestHarness; -use bridge_service::chains::bridge_contracts::BridgeContractError; -use bridge_service::chains::bridge_contracts::BridgeContractEvent; -use bridge_service::chains::ethereum::event_monitoring::EthMonitoring; -use bridge_service::chains::ethereum::types::AtomicBridgeInitiatorMOVE; -use bridge_service::chains::ethereum::utils::send_transaction; -use bridge_service::chains::ethereum::utils::send_transaction_rules; -use bridge_service::chains::{ - ethereum::types::EthAddress, - movement::{ - client_framework::MovementClientFramework, event_monitoring::MovementMonitoring, - utils::MovementAddress, - }, -}; -use bridge_service::types::Amount; -use bridge_service::types::BridgeAddress; -use bridge_service::types::HashLock; -use bridge_service::types::HashLockPreImage; -use futures::StreamExt; -use tracing_subscriber::EnvFilter; - -async fn initiate_eth_bridge_transfer( - config: &Config, - initiator_privatekey: PrivateKeySigner, - recipient: MovementAddress, - hash_lock: HashLock, - amount: Amount, -) -> Result<(), anyhow::Error> { - let initiator_address = initiator_privatekey.address(); - let rpc_provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(EthereumWallet::from(initiator_privatekey)) - .on_builtin(&config.eth.eth_rpc_connection_url()) - .await?; - - let contract = - AtomicBridgeInitiatorMOVE::new(config.eth.eth_initiator_contract.parse()?, &rpc_provider); - - let initiator_address = BridgeAddress(EthAddress(initiator_address)); - - let recipient_address = BridgeAddress(Into::>::into(recipient)); - - let recipient_bytes: [u8; 32] = - recipient_address.0.try_into().expect("Recipient address must be 32 bytes"); - - let call = contract - .initiateBridgeTransfer( - U256::from(amount.0), - FixedBytes(recipient_bytes), - FixedBytes(hash_lock.0), - ) - .value(U256::from(amount.0)) - .from(*initiator_address.0); - - let _ = send_transaction( - call, - initiator_address.0 .0, - &send_transaction_rules(), - config.eth.transaction_send_retries, - config.eth.gas_limit as u128, - ) - .await - .map_err(|e| BridgeContractError::GenericError(format!("Failed to send transaction: {}", e)))?; - Ok(()) -} - -#[tokio::test] -async fn test_bridge_transfer_eth_movement_happy_path() -> Result<(), anyhow::Error> { - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), - ) - .init(); - - let (_eth_client_harness, mut mvt_client_harness, config) = - TestHarness::new_with_eth_and_movement().await?; - - tracing::info!("Init initiator and counter part test account."); - tracing::info!("Use client signer for Mvt and index 2 of config.eth.eth_well_known_account_private_keys array for Eth"); - - // Init mvt addresses - let movement_client_signer_address = mvt_client_harness.movement_client.signer().address(); - - { - let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer_address, 100_000_000).await?; - } - - let recipient_privkey = mvt_client_harness.fund_account().await; - let recipient_address = MovementAddress(recipient_privkey.address()); - - // 1) initialize Eth transfer - tracing::info!("Call initiate_transfer on Eth"); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - initiate_eth_bridge_transfer( - &config, - HarnessEthClient::get_initiator_private_key(&config), - recipient_address, - hash_lock, - amount, - ) - .await - .expect("Failed to initiate bridge transfer"); - - //Wait for the tx to be executed - tracing::info!("Wait for the MVT Locked event."); - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let mut mvt_monitoring = - MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - let event = - tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()).await?; - let bridge_tranfer_id = if let Some(Ok(BridgeContractEvent::Locked(detail))) = event { - detail.bridge_transfer_id - } else { - panic!("Not a Locked event: {event:?}"); - }; - - println!("bridge_tranfer_id : {:?}", bridge_tranfer_id); - println!("hash_lock_pre_image : {:?}", hash_lock_pre_image); - - //send counter complete event. - tracing::info!("Call counterparty_complete_bridge_transfer on MVT."); - mvt_client_harness - .counterparty_complete_bridge_transfer( - recipient_privkey, - bridge_tranfer_id, - hash_lock_pre_image, - ) - .await?; - - let (_, eth_health_rx) = tokio::sync::mpsc::channel(10); - let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); - // Wait for InitialtorCompleted event - tracing::info!("Wait for InitialtorCompleted event."); - loop { - let event = - tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()).await?; - if let Some(Ok(BridgeContractEvent::InitiatorCompleted(_))) = event { - break; - } - } - - Ok(()) -} - -#[tokio::test] -async fn test_movement_event() -> Result<(), anyhow::Error> { - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), - ) - .init(); - - println!("Start test_movement_event",); - - let config = TestHarness::read_bridge_config().await?; - println!("after test_movement_event",); - - use bridge_integration_tests::MovementToEthCallArgs; - - let mut movement_client = MovementClientFramework::new(&config.movement).await.unwrap(); - - let args = MovementToEthCallArgs::default(); - bridge_integration_tests::utils::initiate_bridge_transfer_helper( - &mut movement_client, - args.initiator.0, - args.recipient.clone(), - args.hash_lock.0, - args.amount, - true, - ) - .await - .expect("Failed to initiate bridge transfer"); - - //Wait for the tx to be executed - let _ = tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; - let event_type = format!( - "{}::native_bridge_initiator::BridgeTransferStore", - config.movement.movement_native_address - ); - - let res = test_get_events_by_account_event_handle( - &config.movement.mvt_rpc_connection_url(), - &config.movement.movement_native_address, - &event_type, - ) - .await; - println!("res: {res:?}",); - - let res = fetch_account_events( - &config.movement.mvt_rpc_connection_url(), - &config.movement.movement_native_address, - &event_type, - ) - .await; - println!("res: {res:?}",); - - Ok(()) -} - -async fn test_get_events_by_account_event_handle( - rest_url: &str, - account_address: &str, - event_type: &str, -) { - let url = format!( - "{}/v1/accounts/{}/events/{}/bridge_transfer_initiated_events", - rest_url, account_address, event_type - ); - - println!("url: {:?}", url); - let client = reqwest::Client::new(); - - // Send the GET request - let response = client - .get(&url) - .query(&[("start", "0"), ("limit", "10")]) - .send() - .await - .unwrap() - .text() - .await; - - println!("Account direct response: {response:?}",); -} - -use aptos_sdk::rest_client::Client; -use std::str::FromStr; - -async fn fetch_account_events(rest_url: &str, account_address: &str, event_type: &str) { - // Initialize the RestClient - let node_connection_url = url::Url::from_str(rest_url).unwrap(); - let client = Client::new(node_connection_url); // Use the correct node URL - let native_address = AccountAddress::from_hex_literal(account_address).unwrap(); - - // Get the events for the specified account - let response = client - .get_account_events( - native_address, - event_type, - "bridge_transfer_initiated_events", - Some(1), - None, - ) - .await; - - println!("response{response:?}",); -} diff --git a/protocol-units/bridge/integration-tests/tests/bridge_e2e_test_framework.rs b/protocol-units/bridge/integration-tests/tests/bridge_e2e_test_framework.rs index 1f408e492..245226ffe 100644 --- a/protocol-units/bridge/integration-tests/tests/bridge_e2e_test_framework.rs +++ b/protocol-units/bridge/integration-tests/tests/bridge_e2e_test_framework.rs @@ -1,30 +1,23 @@ -use alloy::primitives::keccak256; use anyhow::Result; -use aptos_types::account_address::AccountAddress; use bridge_integration_tests::{HarnessEthClient, TestHarness}; use bridge_service::{ chains::{ - bridge_contracts::{BridgeContract, BridgeContractEvent}, ethereum::{event_monitoring::EthMonitoring, types::EthAddress}, - movement::{ - client_framework::MovementClientFramework, event_monitoring::MovementMonitoring, - utils::MovementAddress, - }, + movement::{event_monitoring::MovementMonitoring, utils::MovementAddress}, }, - types::{Amount, BridgeAddress, HashLock, HashLockPreImage}, + types::{Amount, BridgeAddress}, }; +use bridge_util::BridgeClientContract; +use bridge_util::BridgeContractEvent; use futures::StreamExt; -use tracing_subscriber::EnvFilter; #[tokio::test] async fn test_bridge_transfer_eth_movement_happy_path() -> Result<(), anyhow::Error> { - tracing_subscriber::fmt().with_env_filter(EnvFilter::new("info")).init(); + //tracing_subscriber::fmt().with_env_filter(EnvFilter::new("info")).init(); - let (eth_client_harness, mut mvt_client_harness, config) = + let (eth_client_harness, mvt_client_harness, config) = TestHarness::new_with_eth_and_movement().await?; - MovementClientFramework::bridge_setup_scripts().await?; - tracing::info!("Init initiator and counterparty test accounts."); tracing::info!("Use client signer for Mvt and index 2 of config.eth.eth_well_known_account_private_keys array for Eth"); @@ -33,63 +26,57 @@ async fn test_bridge_transfer_eth_movement_happy_path() -> Result<(), anyhow::Er { let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer_address, 100_000_000).await?; + faucet_client.fund(movement_client_signer_address, 100_000_000_000).await?; } let recipient_privkey = mvt_client_harness.fund_account().await; let recipient = MovementAddress(recipient_privkey.address()); + let amount = Amount(1); // initiate Eth transfer tracing::info!("Call initiate_transfer on Eth"); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - eth_client_harness + + let res = eth_client_harness .initiate_eth_bridge_transfer( &config, HarnessEthClient::get_initiator_private_key(&config), - recipient, - hash_lock, + recipient.clone(), amount, ) - .await - .expect("Failed to initiate bridge transfer"); + .await; + + assert!(res.is_ok(), "e2e test, Eth initiate transfer failed:{res:?}"); //Wait for the tx to be executed - tracing::info!("Wait for the MVT Locked event."); - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); + tracing::info!("Wait for the Eth Initiated event."); + let (_eth_healthtx, eth_health_rx) = tokio::sync::mpsc::channel(10); + let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); + let event = + tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()).await?; + let (bridge_transfer_id, nonce) = + if let Some(Ok(BridgeContractEvent::Initiated(detail))) = event { + (detail.bridge_transfer_id, detail.nonce) + } else { + panic!("Not an Initiated event: {event:?}"); + }; + + let (_mvt_health_tx, mvt_health_rx) = tokio::sync::mpsc::channel(10); let mut mvt_monitoring = MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - let event = - tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()).await?; - let bridge_tranfer_id = if let Some(Ok(BridgeContractEvent::Locked(detail))) = event { - detail.bridge_transfer_id - } else { - panic!("Not a Locked event: {event:?}"); - }; - - println!("bridge_tranfer_id : {:?}", bridge_tranfer_id); - println!("hash_lock_pre_image : {:?}", hash_lock_pre_image); - - //send counter complete event. - tracing::info!("Call counterparty_complete_bridge_transfer on MVT."); - mvt_client_harness - .counterparty_complete_bridge_transfer( - recipient_privkey, - bridge_tranfer_id, - hash_lock_pre_image, - ) - .await?; - - let (_, eth_health_rx) = tokio::sync::mpsc::channel(10); - let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); - // Wait for InitiatorCompleted event - tracing::info!("Wait for InitiatorCompleted event."); + tracing::info!("Wait for Completed event."); loop { let event = - tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()).await?; - if let Some(Ok(BridgeContractEvent::InitiatorCompleted(_))) = event { + tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()).await?; + if let Some(Ok(BridgeContractEvent::Completed(detail))) = event { + assert_eq!(detail.bridge_transfer_id, bridge_transfer_id); + let addr_vec: Vec = + EthAddress(HarnessEthClient::get_initiator_address(&config)).into(); + assert_eq!(detail.initiator.0, addr_vec); + assert_eq!(detail.recipient, BridgeAddress(recipient)); + assert_eq!(detail.amount, amount); + assert_eq!(detail.nonce, nonce); + break; } } @@ -97,223 +84,71 @@ async fn test_bridge_transfer_eth_movement_happy_path() -> Result<(), anyhow::Er Ok(()) } -#[tokio::test] -async fn test_movement_event() -> Result<(), anyhow::Error> { - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), - ) - .init(); - - println!("Start test_movement_event",); - - let config = TestHarness::read_bridge_config().await?; - println!("after test_movement_event",); - - use bridge_integration_tests::MovementToEthCallArgs; - - let mut movement_client = MovementClientFramework::new(&config.movement).await.unwrap(); - - let args = MovementToEthCallArgs::default(); - - { - let res = BridgeContract::initiate_bridge_transfer( - &mut movement_client, - BridgeAddress(MovementAddress(args.initiator.0)), - BridgeAddress(args.recipient.clone()), - HashLock(args.hash_lock.0), - Amount(args.amount), - ) - .await?; - - tracing::info!("Initiate result: {:?}", res); - } - - //Wait for the tx to be executed - let _ = tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; - let event_type = format!( - "{}::atomic_bridge_initiator::BridgeTransferStore", - config.movement.movement_native_address - ); - - let res = test_get_events_by_account_event_handle( - &config.movement.mvt_rpc_connection_url(), - &config.movement.movement_native_address, - &event_type, - ) - .await; - println!("res: {res:?}",); - - let res = fetch_account_events( - &config.movement.mvt_rpc_connection_url(), - &config.movement.movement_native_address, - &event_type, - ) - .await; - println!("res: {res:?}",); - - Ok(()) -} - -async fn test_get_events_by_account_event_handle( - rest_url: &str, - account_address: &str, - event_type: &str, -) { - let url = format!( - "{}/v1/accounts/{}/events/{}/bridge_transfer_initiated_events", - rest_url, account_address, event_type - ); - - println!("url: {:?}", url); - let client = reqwest::Client::new(); - - // Send the GET request - let response = client - .get(&url) - .query(&[("start", "0"), ("limit", "10")]) - .send() - .await - .unwrap() - .text() - .await; - println!("Account direct response: {response:?}",); -} - -use aptos_sdk::rest_client::Client; -use std::str::FromStr; - -async fn fetch_account_events(rest_url: &str, account_address: &str, event_type: &str) { - // Initialize the RestClient - let node_connection_url = url::Url::from_str(rest_url).unwrap(); - let client = Client::new(node_connection_url); // Use the correct node URL - let native_address = AccountAddress::from_hex_literal(account_address).unwrap(); - - // Get the events for the specified account - let response = client - .get_account_events( - native_address, - event_type, - "bridge_transfer_initiated_events", - Some(1), - None, - ) - .await; - - println!("response{response:?}",); -} - #[tokio::test] async fn test_bridge_transfer_movement_eth_happy_path() -> Result<(), anyhow::Error> { - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), - ) - .init(); + // tracing_subscriber::fmt() + // .with_env_filter( + // EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), + // ) + // .init(); - MovementClientFramework::bridge_setup_scripts().await?; - - let (mut eth_client_harness, mut mvt_client_harness, config) = + let (_eth_client_harness, mut mvt_client_harness, config) = TestHarness::new_with_eth_and_movement().await?; - - let (_, eth_health_rx) = tokio::sync::mpsc::channel(10); - let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); - - // Init mvt addresses - let movement_client_signer_address = mvt_client_harness.movement_client.signer().address(); - - { - let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer_address, 100_000_000).await?; - } - - let recipient_privkey = mvt_client_harness.fund_account().await; - let recipient_address = MovementAddress(recipient_privkey.address()); - - // initiate Eth transfer - tracing::info!("Call initiate_transfer on Eth and set up test"); - let hash_lock_pre_image = HashLockPreImage::random(); - //let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - eth_client_harness - .initiate_eth_bridge_transfer( - &config, - HarnessEthClient::get_initiator_private_key(&config), - recipient_address, - hash_lock, - amount, - ) - .await - .expect("Failed to initiate bridge transfer"); - - tracing::info!("Before init_set_timelock"); - - //mvt_client_harness.init_set_timelock(60).await?; //Set to 1mn - - tracing::info!("Init initiator and counterparty test accounts."); + // must include name of sender channel to avoid it being dropped + let (_mvt_health_tx, mvt_health_rx) = tokio::sync::mpsc::channel(10); + let mut mvt_monitoring = + MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); // Init mvt addresses let movement_client_signer_address = mvt_client_harness.movement_client.signer().address(); - + let initiator_privkey = mvt_client_harness.fund_account().await; + let initiator_address = MovementAddress(initiator_privkey.address()); + tracing::info!("Initiator address: {:?}", initiator_address); + let recipient_address = HarnessEthClient::get_recipient_address(&config).to_vec(); { let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer_address, 100_000_000_000).await?; + faucet_client.fund(movement_client_signer_address, 100_000_000_000_000).await?; + faucet_client.fund(initiator_privkey.address(), 100_000_000_000_000).await?; } + let bridge_fee = mvt_client_harness.get_bridge_fee().await?; - let initiator_account = mvt_client_harness.fund_account().await; - - let counterpart_privekey = HarnessEthClient::get_initiator_private_key(&config); - let counter_party_address = EthAddress(counterpart_privekey.address()); + tracing::info!("Before initiate_bridge_transfer"); + let res = BridgeClientContract::initiate_bridge_transfer( + &mut mvt_client_harness.movement_client, + BridgeAddress(recipient_address.clone()), + Amount(100_000_000_000), + ) + .await?; - // Initialize Movement transfer - let hash_lock_movement_pre_image = HashLockPreImage::random(); - tracing::info!( - "Hash lock pre-image for Movement initiate transfer: {:?}", - hash_lock_movement_pre_image - ); - let hash_lock_movement = HashLock(From::from(keccak256(hash_lock_movement_pre_image))); - tracing::info!("Hash lock for Movement initiate transfer: {:?}", hash_lock_movement); - let amount = 1; - mvt_client_harness - .initiate_bridge_transfer( - &initiator_account, - counter_party_address, - hash_lock_movement, - amount, - ) - .await?; + tracing::info!("Initiate result: {:?}", res); - // Wait for the Eth-side lock event - tracing::info!("Wait for Eth-side Lock event."); - let bridge_tranfer_id; + // Wait for the Movement-side Initiated event + tracing::info!("Wait for Mvt-side Initiated event."); + let bridge_transfer_id; + let nonce; loop { let event = - tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()).await?; - if let Some(Ok(BridgeContractEvent::Locked(detail))) = event { - tracing::info!("Lock details: {:?}", detail); - bridge_tranfer_id = detail.bridge_transfer_id; + tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()).await?; + if let Some(Ok(BridgeContractEvent::Initiated(detail))) = event { + tracing::info!("Initiated details: {:?}", detail); + bridge_transfer_id = detail.bridge_transfer_id; + nonce = detail.nonce; break; } } - tracing::info!("Bridge transfer ID from Eth Lock event: {:?}", bridge_tranfer_id); - - // Complete transfer on Eth - tracing::info!("Before Eth counterparty complete with preimage {:?}", hash_lock_pre_image); - eth_client_harness - .eth_client - .counterparty_complete_bridge_transfer(bridge_tranfer_id, hash_lock_pre_image) - .await?; - - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let mut mvt_monitoring = - MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - - tracing::info!("Wait for InitiatorCompleted event."); + tracing::info!("Bridge transfer ID from Mvt Initiated event: {:?}", bridge_transfer_id); + tracing::info!("Wait for Completed event."); + let (_eth_health_tx, eth_health_rx) = tokio::sync::mpsc::channel(10); + let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); loop { let event = - tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()).await?; - if let Some(Ok(BridgeContractEvent::InitiatorCompleted(_))) = event { + tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()).await?; + if let Some(Ok(BridgeContractEvent::Completed(detail))) = event { + assert_eq!(detail.bridge_transfer_id, bridge_transfer_id); + // assert_eq!(detail.initiator.0, initiator_address.0.to_vec()); + assert_eq!(detail.recipient.0 .0.to_vec(), recipient_address); + assert_eq!(detail.amount, Amount(100_000_000_000 - bridge_fee)); + assert_eq!(detail.nonce, nonce); break; } } diff --git a/protocol-units/bridge/integration-tests/tests/bridge_rest.rs b/protocol-units/bridge/integration-tests/tests/bridge_rest.rs index eeb1d73bb..6326d0700 100644 --- a/protocol-units/bridge/integration-tests/tests/bridge_rest.rs +++ b/protocol-units/bridge/integration-tests/tests/bridge_rest.rs @@ -17,16 +17,27 @@ async fn test_rest_service_health_endpoint() -> Result<(), anyhow::Error> { let mock_config = Config::default(); // Create the REST service, unwrapping the result - let (health_tx, mut health_rx) = tokio::sync::mpsc::channel(10); - let rest_service = Arc::new(BridgeRest::new(&mock_config.movement, health_tx)?); + let (l1_health_tx, mut l1_health_rx) = tokio::sync::mpsc::channel(10); + let (l2_health_tx, mut l2_health_rx) = tokio::sync::mpsc::channel(10); + let rest_service = + Arc::new(BridgeRest::new(&mock_config.movement, l1_health_tx, l2_health_tx)?); let rest_service_for_task = Arc::clone(&rest_service); //simulate the bridge loop part - let bridge_loop_future = tokio::spawn(async move { + let l1_bridge_loop_future = tokio::spawn(async move { loop { - if let Some(oneshot_tx) = health_rx.recv().await { - if let Err(err) = oneshot_tx.send("OK".to_string()) { + if let Some(oneshot_tx) = l1_health_rx.recv().await { + if let Err(err) = oneshot_tx.send(true) { + tracing::warn!("Heal check oneshot channel closed abnormally :{err:?}"); + } + } + } + }); + let l2_bridge_loop_future = tokio::spawn(async move { + loop { + if let Some(oneshot_tx) = l2_health_rx.recv().await { + if let Err(err) = oneshot_tx.send(true) { tracing::warn!("Heal check oneshot channel closed abnormally :{err:?}"); } } @@ -46,7 +57,8 @@ async fn test_rest_service_health_endpoint() -> Result<(), anyhow::Error> { // Abort the REST service task rest_service_future.abort(); - bridge_loop_future.abort(); + l1_bridge_loop_future.abort(); + l2_bridge_loop_future.abort(); Ok(()) } diff --git a/protocol-units/bridge/integration-tests/tests/client_eth_tests.rs b/protocol-units/bridge/integration-tests/tests/client_eth_tests.rs index 7b449476a..da6bde9ed 100644 --- a/protocol-units/bridge/integration-tests/tests/client_eth_tests.rs +++ b/protocol-units/bridge/integration-tests/tests/client_eth_tests.rs @@ -1,210 +1,103 @@ -use alloy::primitives::keccak256; -use alloy::primitives::Address; -use anyhow::Result; use bridge_integration_tests::HarnessEthClient; use bridge_integration_tests::HarnessMvtClient; use bridge_integration_tests::TestHarness; -use bridge_service::chains::bridge_contracts::{BridgeContract, BridgeContractEvent}; -use bridge_service::chains::ethereum::{event_monitoring::EthMonitoring, types::EthAddress}; -use bridge_service::types::{Amount, BridgeAddress, BridgeTransferId, HashLock, HashLockPreImage}; +use bridge_service::chains::ethereum::event_monitoring::EthMonitoring; +use bridge_service::chains::ethereum::types::EthAddress; +use bridge_service::chains::movement::utils::MovementAddress; +use bridge_service::types::Amount; +use bridge_util::chains::bridge_contracts::BridgeRelayerContract; +use bridge_util::types::BridgeAddress; +use bridge_util::BridgeContractEvent; use futures::StreamExt; -use std::str::FromStr; use tokio::{self}; #[tokio::test] -async fn test_eth_client_counterparty_complete_transfer() { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut eth_client_harness, config) = - TestHarness::new_only_eth().await.expect("Bridge config file not set"); - - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - - let transfer_id = BridgeTransferId::gen_unique_hash(&mut rand::rngs::OsRng); - let res = BridgeContract::lock_bridge_transfer( - &mut eth_client_harness.eth_client, - transfer_id, - hash_lock, - BridgeAddress(vec![3; 32]), - BridgeAddress(EthAddress(HarnessEthClient::get_recipeint_address(&config))), - amount, - ) - .await; - assert!(res.is_ok()); - - BridgeContract::counterparty_complete_bridge_transfer( - &mut eth_client_harness.eth_client, - transfer_id, - hash_lock_pre_image, - ) - .await - .expect("Failed to complete bridge transfer"); -} - -#[tokio::test] -async fn test_eth_client_lock_transfer() -> Result<(), anyhow::Error> { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut eth_client_harness, config) = - TestHarness::new_only_eth().await.expect("Bridge config file not set"); - - // Call lock transfer Eth - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - let transfer_id = BridgeTransferId::gen_unique_hash(&mut rand::rngs::OsRng); - - let res = eth_client_harness - .eth_client - .lock_bridge_transfer( - transfer_id, - hash_lock, - BridgeAddress(vec![3; 32]), - BridgeAddress(EthAddress(HarnessEthClient::get_recipeint_address(&config))), - amount, - ) - .await; - - assert!(res.is_ok(), "lock_bridge_transfer failed because: {res:?}"); - - Ok(()) -} - -#[tokio::test] -async fn test_eth_client_initiate_transfer() { +async fn test_eth_client_initiate_bridge_transfer() { let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); let (eth_client_harness, config) = TestHarness::new_only_eth().await.expect("Bridge config file not set"); - - let recipient = HarnessMvtClient::gen_aptos_account(); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - - let res = eth_client_harness - .initiate_eth_bridge_transfer( - &config, - HarnessEthClient::get_initiator_private_key(&config), - bridge_service::chains::movement::utils::MovementAddress(recipient.address()), - hash_lock, - Amount(1), - ) - .await; - assert!(res.is_ok(), "initiate_bridge_transfer failed because: {res:?}"); -} - -#[tokio::test] -async fn test_eth_client_initiator_complete_transfer() { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - - let (mut eth_client_harness, config) = - TestHarness::new_only_eth().await.expect("Bridge config file not set"); let (_eth_health_tx, eth_health_rx) = tokio::sync::mpsc::channel(10); let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); let recipient = HarnessMvtClient::gen_aptos_account(); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let res = eth_client_harness .initiate_eth_bridge_transfer( &config, HarnessEthClient::get_initiator_private_key(&config), - bridge_service::chains::movement::utils::MovementAddress(recipient.address()), - hash_lock, + MovementAddress(recipient.address()), Amount(1), ) .await; + assert!(res.is_ok(), "initiate_bridge_transfer failed because: {res:?}"); - assert!(res.is_ok(), "initiate_bridge_transfer failed: {:?}", res.unwrap_err()); - - // Wait for the tx to be executed - tracing::info!("Wait for the ETH Initiated event."); - - // Use timeout to wait for the next event - let event_option = - tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()) + // Wait for the Eth-side Initiated event + tracing::info!("Wait for Eth-side Initiated event."); + loop { + let event = tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()) .await - .expect("Timeout while waiting for the ETH Locked event"); - - // Check if we received an event (Option) and handle the Result inside it - let bridge_transfer_id = match event_option { - Some(Ok(BridgeContractEvent::Initiated(detail))) => detail.bridge_transfer_id, - Some(Err(e)) => panic!("Error in bridge contract event: {:?}", e), - None => panic!("No event received"), - _ => panic!("Not a an Initiated event: {:?}", event_option), - }; - - tracing::info!("Received bridge_transfer_id: {}", bridge_transfer_id); - - let res = eth_client_harness - .eth_client - .initiator_complete_bridge_transfer(bridge_transfer_id, hash_lock_pre_image) - .await; - - assert!(res.is_ok(), "initiator_complete_bridge_transfer failed: {:?}", res.unwrap_err()); + .expect("Wait for completed event timeout."); + if let Some(Ok(BridgeContractEvent::Initiated(detail))) = event { + tracing::info!("Initiated details: {:?}", detail); + let recipient_address = + BridgeAddress(Into::>::into(MovementAddress(recipient.address()))); + assert_eq!(recipient_address, detail.recipient, "Bad recipient in Initiated event"); + break; + } + } } #[tokio::test] -async fn test_eth_client_refund_transfer() { +async fn test_eth_client_complete_bridge_transfer() { let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); let (mut eth_client_harness, config) = TestHarness::new_only_eth().await.expect("Bridge config file not set"); - let (_, eth_health_rx) = tokio::sync::mpsc::channel(10); + let (_eth_health_tx, eth_health_rx) = tokio::sync::mpsc::channel(10); let mut eth_monitoring = EthMonitoring::build(&config.eth, eth_health_rx).await.unwrap(); - // let rpc_provider = eth_client_harness.rpc_provider().await; + let initiator_address = HarnessMvtClient::gen_aptos_account().address(); + let recipeint_address = + EthAddress(HarnessEthClient::get_recipient_private_key(&config).address()); - // let init_contract = bridge_service::chains::ethereum::types::AtomicBridgeInitiatorMOVE::new( - // Address::from_str(&config.eth.eth_initiator_contract).unwrap(), - // rpc_provider.clone(), - // ); + let nonce = TestHarness::create_nonce(); + let amount = Amount(2); - // Approve the ETH initiator contract to spend Amount of MOVE - // let approve_call = init_contract - // .refundBridgeTransfer(alloy::primitives::FixedBytes([2; 32])) - // .from(eth_client_harness.signer_address()); - // approve_call.send().await.unwrap().get_receipt().await.unwrap(); + let transfer_id = HarnessEthClient::calculate_bridge_transfer_id( + initiator_address, + *recipeint_address, + amount, + nonce, + ); - let recipient = HarnessMvtClient::gen_aptos_account(); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); + tracing::info!("Transfer ID Eth side: {:?}", transfer_id); let res = eth_client_harness - .initiate_eth_bridge_transfer( - &config, - HarnessEthClient::get_initiator_private_key(&config), - bridge_service::chains::movement::utils::MovementAddress(recipient.address()), - hash_lock, - Amount(1), + .eth_client + .complete_bridge_transfer( + transfer_id, + BridgeAddress(initiator_address.into()), + BridgeAddress(recipeint_address), + amount, + nonce, ) .await; - assert!(res.is_ok(), "initiate_bridge_transfer failed: {:?}", res.unwrap_err()); + assert!(res.is_ok(), "complete_bridge_transfer failed: {:?}", res.unwrap_err()); - // Wait for the tx to be executed - tracing::info!("Wait for the ETH Initiated event."); - - // Use timeout to wait for the next event - let event_option = - tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()) + // Wait for the Eth-side Completed event + tracing::info!("Wait for Eth-side Completed event."); + loop { + let event = tokio::time::timeout(std::time::Duration::from_secs(30), eth_monitoring.next()) .await - .expect("Timeout while waiting for the ETH Initiated event"); - - // Check if we received an event (Option) and handle the Result inside it - let bridge_transfer_id = match event_option { - Some(Ok(BridgeContractEvent::Initiated(detail))) => detail.bridge_transfer_id, - Some(Err(e)) => panic!("Error in bridge contract event: {:?}", e), - None => panic!("No event received"), - _ => panic!("Not a an Initiated event: {:?}", event_option), - }; - - tracing::info!("Received bridge_transfer_id: {}", bridge_transfer_id); - - // Wait end of timelock - tokio::time::sleep(tokio::time::Duration::from_secs(20)).await; - - let res = eth_client_harness.eth_client.refund_bridge_transfer(bridge_transfer_id).await; - - assert!(res.is_ok(), "initiator_complete_bridge_transfer failed: {:?}", res.unwrap_err()); + .expect("Wait for completed event timeout."); + if let Some(Ok(BridgeContractEvent::Completed(detail))) = event { + tracing::info!("Completed details: {:?}", detail); + assert_eq!( + transfer_id, detail.bridge_transfer_id, + "Bad transfer id in completed event" + ); + assert_eq!(nonce, detail.nonce, "Bad nonce in completed event"); + break; + } + } } diff --git a/protocol-units/bridge/integration-tests/tests/client_mvt_tests.rs b/protocol-units/bridge/integration-tests/tests/client_mvt_tests.rs index 5ee88574f..45b2f0878 100644 --- a/protocol-units/bridge/integration-tests/tests/client_mvt_tests.rs +++ b/protocol-units/bridge/integration-tests/tests/client_mvt_tests.rs @@ -1,167 +1,80 @@ -use alloy::primitives::keccak256; use anyhow::Result; use aptos_sdk::coin_client::CoinClient; -use aptos_sdk::types::account_address::AccountAddress; -use bridge_integration_tests::utils as test_utils; -use bridge_integration_tests::{EthToMovementCallArgs, MovementToEthCallArgs, TestHarness}; -use bridge_service::chains::bridge_contracts::BridgeContractEvent; +use bridge_integration_tests::HarnessEthClient; +use bridge_integration_tests::HarnessMvtClient; +use bridge_integration_tests::TestHarness; use bridge_service::chains::movement::event_monitoring::MovementMonitoring; use bridge_service::{ - chains::{ - bridge_contracts::BridgeContract, - movement::utils::{MovementAddress, MovementHash}, - }, - types::{Amount, BridgeAddress, BridgeTransferId, HashLock, HashLockPreImage}, + chains::{ethereum::types::EthAddress, movement::utils::MovementAddress}, + types::{Amount, BridgeAddress}, }; -use chrono::Utc; +use bridge_util::BridgeClientContract; +use bridge_util::BridgeContractEvent; +use bridge_util::BridgeRelayerContract; use futures::StreamExt; -use tokio::time::{sleep, Duration}; use tokio::{self}; -use tracing::info; - -#[tokio::test] -async fn test_movement_client_counterparty_complete_transfer() -> Result<(), anyhow::Error> { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut mvt_client_harness, _config) = - TestHarness::new_with_movement().await.expect("Bridge config file not set"); - let hash_lock_pre_image = HashLockPreImage::random(); - let hash_lock = HashLock(From::from(keccak256(hash_lock_pre_image))); - let amount = Amount(1); - let transfer_id = BridgeTransferId::gen_unique_hash(&mut rand::rngs::OsRng); - let initiator = b"32Be343B94f860124dC4fEe278FDCBD38C102D88".to_vec(); - let recipient = AccountAddress::new(*b"0x00000000000000000000000000face"); - - let coin_client = CoinClient::new(&mvt_client_harness.rest_client); - let movement_client_signer = mvt_client_harness.movement_client.signer(); - { - let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); - faucet_client.fund(movement_client_signer.address(), 100_000_000).await?; - faucet_client.fund(recipient, 100_000_000).await?; - } - let balance = coin_client.get_account_balance(&movement_client_signer.address()).await?; - assert!( - balance >= 100_000_000, - "Expected Movement Client to have at least 100_000_000, but found {}", - balance - ); - - mvt_client_harness - .movement_client - .lock_bridge_transfer( - transfer_id, - hash_lock, - BridgeAddress(initiator.clone()), - BridgeAddress(MovementAddress(recipient.clone())), - amount, - ) - .await - .expect("Failed to lock bridge transfer"); - - let details = BridgeContract::get_bridge_transfer_details_counterparty( - &mut mvt_client_harness.movement_client, - transfer_id, - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - assert_eq!(details.state, 1, "Bridge transfer should be pending."); - info!("Bridge transfer details: {:?}", details); - - let secret = b"secret"; - let mut padded_secret = [0u8; 32]; - padded_secret[..secret.len()].copy_from_slice(secret); - - BridgeContract::counterparty_complete_bridge_transfer( - &mut mvt_client_harness.movement_client, - transfer_id, - hash_lock_pre_image, - ) - .await - .expect("Failed to complete bridge transfer"); - - let details = BridgeContract::get_bridge_transfer_details_counterparty( - &mut mvt_client_harness.movement_client, - transfer_id, - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - assert_eq!(details.bridge_transfer_id.0, transfer_id.0); - assert_eq!(details.hash_lock.0, hash_lock.0); - assert_eq!(&details.initiator.0, &initiator, "Initiator address does not match"); - assert_eq!(details.recipient.0, MovementAddress(recipient)); - assert_eq!(details.amount.0, *amount); - assert_eq!(details.state, 2, "Bridge transfer is supposed to be completed but it's not."); - - Ok(()) -} #[tokio::test] async fn test_movement_client_initiate_transfer() -> Result<(), anyhow::Error> { let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); let (mut mvt_client_harness, config) = TestHarness::new_with_movement().await.expect("Bridge config file not set"); - let args = MovementToEthCallArgs::default(); + let (_mvt_health_tx, mvt_health_rx) = tokio::sync::mpsc::channel(10); + let mut mvt_monitoring = + MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); + let recipient_address = HarnessEthClient::get_recipient_address(&config).to_vec(); let test_result = async { - test_utils::fund_and_check_balance_framework(&mut mvt_client_harness, 100_000_000_000) + mvt_client_harness + .fund_signer_and_check_balance_framework(100_000_000_000) .await?; - { - let res = BridgeContract::initiate_bridge_transfer( + tracing::info!("Before initiate_bridge_transfer"); + let res = BridgeClientContract::initiate_bridge_transfer( &mut mvt_client_harness.movement_client, - BridgeAddress(MovementAddress(args.initiator.0)), - BridgeAddress(args.recipient.clone()), - HashLock(args.hash_lock.0), - Amount(args.amount), + BridgeAddress(recipient_address.clone()), + Amount(100_000_000_000), ) .await?; tracing::info!("Initiate result: {:?}", res); } - // Wait for the tx to be executed - tracing::info!("Wait for the Movement Initiated event."); - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let mut mvt_monitoring = - MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - - // Use timeout to wait for the next event - let event_option = - tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()) - .await - .expect("Timeout while waiting for the Movement Initiated event"); + let bridge_fee = mvt_client_harness.get_bridge_fee().await?; + + // Wait for the Movement Initiated event + tracing::info!("Wait for Movement-side Initiated event."); + let mut received_event = None; + + // Use a loop to wait for the Initiated event + loop { + let event = + tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()) + .await + .expect("Wait for initiated event timeout."); + + if let Some(Ok(BridgeContractEvent::Initiated(detail))) = event { + tracing::info!("Initiated details: {:?}", detail); + + received_event = Some(( + detail.bridge_transfer_id, + detail.initiator, + detail.recipient, + detail.amount, + detail.nonce, + )); + break; + } + } - // Check if we received an event (Option) and handle the Result inside it - let bridge_transfer_id = match event_option { - Some(Ok(BridgeContractEvent::Initiated(detail))) => detail.bridge_transfer_id, - Some(Err(e)) => panic!("Error in bridge contract event: {:?}", e), - None => panic!("No event received"), - _ => panic!("Not a an Initiated event: {:?}", event_option), - }; + let (bridge_transfer_id, initiator, recipient, amount, _nonce) = + received_event.expect("No initiated event received"); tracing::info!("Received bridge_transfer_id: {:?}", bridge_transfer_id); - let details = BridgeContract::get_bridge_transfer_details_initiator( - &mut mvt_client_harness.movement_client, - bridge_transfer_id, - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - assert_eq!(details.state, 1, "Bridge transfer should be initiated."); - - test_utils::assert_counterparty_bridge_transfer_details_framework( - &details, - details.initiator.to_string(), - details.recipient.to_vec(), - details.amount.0, - details.hash_lock.0, - details.time_lock.0, - ); + assert_eq!(initiator.0 .0, mvt_client_harness.signer_address()); + assert_eq!(recipient, BridgeAddress(recipient_address)); + assert_eq!(amount, Amount(100_000_000_000 - bridge_fee)); Ok(()) } @@ -171,19 +84,44 @@ async fn test_movement_client_initiate_transfer() -> Result<(), anyhow::Error> { } #[tokio::test] -async fn test_movement_client_abort_transfer() -> Result<(), anyhow::Error> { +async fn test_movement_client_complete_transfer() -> Result<(), anyhow::Error> { let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut mvt_client_harness, _config) = + let (mut mvt_client_harness, config) = TestHarness::new_with_movement().await.expect("Bridge config file not set"); - let args = EthToMovementCallArgs::default(); + let (_mvt_health_tx, mvt_health_rx) = tokio::sync::mpsc::channel(10); + let mut mvt_monitoring = + MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); + + // Set initiator as bytes + let initiator = EthAddress(HarnessEthClient::get_initiator_address(&config)); + + // Set recipient address + let recipient = HarnessMvtClient::gen_aptos_account().address(); + + // Set amount to 1 + let amount = Amount(100_000_000_000); + + // Random nonce + let incoming_nonce = TestHarness::create_nonce(); + + let bridge_transfer_id = HarnessMvtClient::calculate_bridge_transfer_id( + initiator.clone().0, + recipient, + amount, + incoming_nonce, + ); + let coin_client = CoinClient::new(&mvt_client_harness.rest_client); let movement_client_signer = mvt_client_harness.movement_client.signer(); + // Fund accounts { let faucet_client = mvt_client_harness.faucet_client.write().unwrap(); faucet_client.fund(movement_client_signer.address(), 100_000_000).await?; + faucet_client.fund(recipient, 100_000_000).await?; } + // Assert the balance is sufficient let balance = coin_client.get_account_balance(&movement_client_signer.address()).await?; assert!( balance >= 100_000_000, @@ -191,199 +129,44 @@ async fn test_movement_client_abort_transfer() -> Result<(), anyhow::Error> { balance ); - mvt_client_harness - .movement_client - .lock_bridge_transfer( - BridgeTransferId(args.bridge_transfer_id.0), - HashLock(args.hash_lock.0), - BridgeAddress(args.initiator.clone()), - BridgeAddress(args.recipient.clone().into()), - Amount(args.amount), - ) - .await - .expect("Failed to lock bridge transfer"); - - let details = BridgeContract::get_bridge_transfer_details_counterparty( - &mut mvt_client_harness.movement_client, - BridgeTransferId(args.bridge_transfer_id.0), - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - assert_eq!(details.state, 1, "Bridge transfer should be pending."); - - sleep(Duration::from_secs(20)).await; - - let secret = b"secret"; - let mut padded_secret = [0u8; 32]; - padded_secret[..secret.len()].copy_from_slice(secret); - - BridgeContract::abort_bridge_transfer( + // Call the complete_bridge_transfer function + BridgeRelayerContract::complete_bridge_transfer( &mut mvt_client_harness.movement_client, - BridgeTransferId(args.bridge_transfer_id.0), + bridge_transfer_id, + BridgeAddress(initiator.clone().to_vec()), + BridgeAddress(MovementAddress(recipient)), + amount, + incoming_nonce, ) .await .expect("Failed to complete bridge transfer"); - let details = BridgeContract::get_bridge_transfer_details_counterparty( - &mut mvt_client_harness.movement_client, - BridgeTransferId(args.bridge_transfer_id.0), - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - assert_eq!(details.bridge_transfer_id.0, args.bridge_transfer_id.0); - assert_eq!(details.hash_lock.0, args.hash_lock.0); - assert_eq!(&details.initiator.0, &args.initiator, "Initiator address does not match"); - assert_eq!(details.recipient.0, args.recipient); - assert_eq!(details.amount.0, args.amount); - assert_eq!(details.state, 3, "Bridge transfer is supposed to be cancelled but it's not."); - - Ok(()) -} - -// Failing with EINVALID_PRE_IMAGE(0x1). Client and unit tests for modules used in client_movement_eth pass. -#[tokio::test] -async fn test_movement_client_initiator_complete_transfer() -> Result<(), anyhow::Error> { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut mvt_client_harness, config) = - TestHarness::new_with_movement().await.expect("Bridge config file not set"); - let args = MovementToEthCallArgs::default(); - test_utils::fund_and_check_balance_framework(&mut mvt_client_harness, 100_000_000_000).await?; - { - let res = BridgeContract::initiate_bridge_transfer( - &mut mvt_client_harness.movement_client, - BridgeAddress(MovementAddress(args.initiator.0)), - BridgeAddress(args.recipient.clone()), - HashLock(args.hash_lock.0), - Amount(args.amount), - ) - .await?; - - tracing::info!("Initiate result: {:?}", res); - } - - // Wait for the tx to be executed - tracing::info!("Wait for the Movement Initiated event."); - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let mut mvt_monitoring = - MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - - // Use timeout to wait for the next event - let event_option = - tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()) + // Wait for the Movement-side Completed event + tracing::info!("Wait for Movement-side Completed event."); + loop { + let event = tokio::time::timeout(std::time::Duration::from_secs(30), mvt_monitoring.next()) .await - .expect("Timeout while waiting for the Movement Initiated event"); - - // Check if we received an event (Option) and handle the Result inside it - let bridge_transfer_id = match event_option { - Some(Ok(BridgeContractEvent::Initiated(detail))) => detail.bridge_transfer_id, - Some(Err(e)) => panic!("Error in bridge contract event: {:?}", e), - None => panic!("No event received"), - _ => panic!("Not a an Initiated event: {:?}", event_option), - }; - - tracing::info!("Received bridge_transfer_id: {:?}", bridge_transfer_id); - - BridgeContract::initiator_complete_bridge_transfer( - &mut mvt_client_harness.movement_client, - BridgeTransferId(bridge_transfer_id.0), - HashLockPreImage(args.pre_image), - ) - .await - .expect("Failed to complete bridge transfer"); - - let details = BridgeContract::get_bridge_transfer_details_initiator( - &mut mvt_client_harness.movement_client, - BridgeTransferId(bridge_transfer_id.0), - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - info!("Bridge transfer details: {:?}", details); - - test_utils::assert_counterparty_bridge_transfer_details_framework( - &details, - details.initiator.to_string(), - details.recipient.to_vec(), - details.amount.0, - details.hash_lock.0, - details.time_lock.0, - ); - - assert_eq!(details.state, 2, "Bridge transfer should be completed."); - - Ok(()) -} - -#[tokio::test] -async fn test_movement_client_refund_transfer() -> Result<(), anyhow::Error> { - let _ = tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).try_init(); - let (mut mvt_client_harness, config) = - TestHarness::new_with_movement().await.expect("Bridge config file not set"); - let args = MovementToEthCallArgs::default(); - test_utils::fund_and_check_balance_framework(&mut mvt_client_harness, 100_000_000_000).await?; - - { - let res = BridgeContract::initiate_bridge_transfer( - &mut mvt_client_harness.movement_client, - BridgeAddress(MovementAddress(args.initiator.0)), - BridgeAddress(args.recipient.clone()), - HashLock(args.hash_lock.0), - Amount(args.amount), - ) - .await?; - - tracing::info!("Initiate result: {:?}", res); + .expect("Wait for completed event timeout."); + if let Some(Ok(BridgeContractEvent::Completed(detail))) = event { + tracing::info!("Completed details: {:?}", detail); + assert_eq!( + detail.bridge_transfer_id, bridge_transfer_id, + "Bad transfer id in completed event" + ); + assert_eq!(detail.nonce, incoming_nonce, "Bad nonce in completed event"); + assert_eq!(detail.amount, amount, "Bad amount in completed event"); + assert_eq!( + detail.initiator, + BridgeAddress(initiator.to_vec()), + "Bad initiator address in completed event" + ); + assert_eq!( + detail.recipient.0 .0, recipient, + "Bad recipient address in completed event" + ); + break; + } } - // Wait for the tx to be executed - tracing::info!("Wait for the Movement Initiated event."); - let (_, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let mut mvt_monitoring = - MovementMonitoring::build(&config.movement, mvt_health_rx).await.unwrap(); - - // Use timeout to wait for the next event - let event_option = - tokio::time::timeout(std::time::Duration::from_secs(120), mvt_monitoring.next()) - .await - .expect("Timeout while waiting for the Movement Initiated event"); - - // Check if we received an event (Option) and handle the Result inside it - let bridge_transfer_id = match event_option { - Some(Ok(BridgeContractEvent::Initiated(detail))) => detail.bridge_transfer_id, - Some(Err(e)) => panic!("Error in bridge contract event: {:?}", e), - None => panic!("No event received"), - _ => panic!("Not a an Initiated event: {:?}", event_option), - }; - - tracing::info!("Received bridge_transfer_id: {:?}", bridge_transfer_id); - - sleep(Duration::from_secs(15)).await; - - info!("Current timestamp: {:?}", Utc::now().timestamp()); - - BridgeContract::refund_bridge_transfer( - &mut mvt_client_harness.movement_client, - bridge_transfer_id, - ) - .await - .expect("Failed to refund bridge transfer"); - - let details = BridgeContract::get_bridge_transfer_details_initiator( - &mut mvt_client_harness.movement_client, - bridge_transfer_id, - ) - .await - .expect("Failed to get bridge transfer details") - .expect("Expected to find bridge transfer details, but got None"); - - tracing::info!("Transfer details after refund: {:?}", details); - - assert_eq!(details.state, 3, "Bridge transfer should be refunded."); - Ok(()) } diff --git a/protocol-units/bridge/move-modules/scripts/enable_bridge_feature.move b/protocol-units/bridge/move-modules/scripts/enable_bridge_feature.move index 961b9297b..b6742646d 100644 --- a/protocol-units/bridge/move-modules/scripts/enable_bridge_feature.move +++ b/protocol-units/bridge/move-modules/scripts/enable_bridge_feature.move @@ -6,7 +6,7 @@ script { let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); let enabled_blob: vector = vector[ - features::get_atomic_bridge_feature() + features::get_native_bridge_feature() ]; let disabled_blob: vector = vector[]; diff --git a/protocol-units/bridge/move-modules/scripts/set_counterparty_time_lock_duration.move b/protocol-units/bridge/move-modules/scripts/set_counterparty_time_lock_duration.move deleted file mode 100644 index 20da131f4..000000000 --- a/protocol-units/bridge/move-modules/scripts/set_counterparty_time_lock_duration.move +++ /dev/null @@ -1,9 +0,0 @@ -script { - use aptos_framework::aptos_governance; - use aptos_framework::atomic_bridge_configuration; - - fun set_counterparty_time_lock_duration(core_resources: &signer, new_timelock: u64) { - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @aptos_framework); - atomic_bridge_configuration::set_counterparty_time_lock_duration(&framework_signer, new_timelock); - } -} diff --git a/protocol-units/bridge/move-modules/scripts/set_initiator_time_lock_duration.move b/protocol-units/bridge/move-modules/scripts/set_initiator_time_lock_duration.move deleted file mode 100644 index 0e779bdcb..000000000 --- a/protocol-units/bridge/move-modules/scripts/set_initiator_time_lock_duration.move +++ /dev/null @@ -1,9 +0,0 @@ -script { - use aptos_framework::aptos_governance; - use aptos_framework::atomic_bridge_configuration; - - fun set_initiator_time_lock_duration(core_resources: &signer, new_timelock: u64) { - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @aptos_framework); - atomic_bridge_configuration::set_initiator_time_lock_duration(&framework_signer, new_timelock); - } -} diff --git a/protocol-units/bridge/move-modules/scripts/store_mint_burn_caps.move b/protocol-units/bridge/move-modules/scripts/store_mint_burn_caps.move index 0f43463f0..79bf135d1 100644 --- a/protocol-units/bridge/move-modules/scripts/store_mint_burn_caps.move +++ b/protocol-units/bridge/move-modules/scripts/store_mint_burn_caps.move @@ -1,13 +1,13 @@ script { use aptos_framework::aptos_governance; use aptos_framework::transaction_fee; - use aptos_framework::atomic_bridge; + use aptos_framework::native_bridge; fun store_mint_burn_caps(core_resources: &signer) { let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @aptos_framework); - let (mint, burn) = transaction_fee::copy_capabilities_for_bridge(&framework_signer); + let (mint, burn) = transaction_fee::copy_capabilities_for_native_bridge(&framework_signer); - atomic_bridge::store_aptos_coin_mint_cap(&framework_signer, mint); - atomic_bridge::store_aptos_coin_burn_cap(&framework_signer, burn); + native_bridge::store_aptos_coin_mint_cap(&framework_signer, mint); + native_bridge::store_aptos_coin_burn_cap(&framework_signer, burn); } } diff --git a/protocol-units/bridge/move-modules/scripts/update_bridge_fee.move b/protocol-units/bridge/move-modules/scripts/update_bridge_fee.move new file mode 100644 index 000000000..866ff9e95 --- /dev/null +++ b/protocol-units/bridge/move-modules/scripts/update_bridge_fee.move @@ -0,0 +1,9 @@ +script { + use aptos_framework::aptos_governance; + use aptos_framework::native_bridge; + + fun update_bridge_fee(core_resources: &signer, new_fee: u64) { + let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @aptos_framework); + native_bridge::update_bridge_fee(&framework_signer, new_fee); + } +} \ No newline at end of file diff --git a/protocol-units/bridge/move-modules/scripts/update_bridge_operator.move b/protocol-units/bridge/move-modules/scripts/update_bridge_relayer.move similarity index 55% rename from protocol-units/bridge/move-modules/scripts/update_bridge_operator.move rename to protocol-units/bridge/move-modules/scripts/update_bridge_relayer.move index dc4ee8422..587df3cba 100644 --- a/protocol-units/bridge/move-modules/scripts/update_bridge_operator.move +++ b/protocol-units/bridge/move-modules/scripts/update_bridge_relayer.move @@ -1,11 +1,11 @@ script { use aptos_framework::aptos_account; use aptos_framework::aptos_governance; - use aptos_framework::atomic_bridge_configuration; + use aptos_framework::native_bridge; - fun update_bridge_operator(core_resources: &signer, new_operator: address) { + fun update_bridge_relayer(core_resources: &signer, new_relayer: address) { let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @aptos_framework); - atomic_bridge_configuration::update_bridge_operator(&framework_signer, new_operator); + native_bridge::update_bridge_relayer(&framework_signer, new_relayer); aptos_account::create_account(@0x00000000000000000000000000face); } } diff --git a/protocol-units/bridge/service/Cargo.toml b/protocol-units/bridge/service/Cargo.toml index 9fac0a639..9773276b0 100644 --- a/protocol-units/bridge/service/Cargo.toml +++ b/protocol-units/bridge/service/Cargo.toml @@ -9,10 +9,17 @@ homepage.workspace = true publish.workspace = true rust-version.workspace = true +[[bin]] +name = "start_indexer" +path = "bin/start_indexer.rs" + + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bridge-grpc = { workspace = true, features = ["server"] } bridge-util = { workspace = true } bridge-indexer-db = { workspace = true } +bridge-config = { workspace = true } alloy-primitives = { workspace = true } anyhow = { workspace = true } async-trait = "0.1.80" @@ -23,7 +30,7 @@ futures-timer = "3.0.3" hex = { workspace = true } thiserror.workspace = true tokio = { workspace = true, features = ["full"] } -tokio-stream = "0.1.16" +tokio-stream = { workspace = true } tracing.workspace = true rand.workspace = true rand_chacha = "0.2.2" @@ -58,9 +65,7 @@ derive-new = { workspace = true } async-stream = { workspace = true } #To be removed after send_transaction refactor -bridge-grpc = { workspace = true, features = ["server"] } mcr-settlement-client = { workspace = true } -bridge-config = { workspace = true } godfig = { workspace = true } dot-movement = { workspace = true } diff --git a/protocol-units/bridge/service/abis/AtomicBridgeCounterpartyMOVE.json b/protocol-units/bridge/service/abis/AtomicBridgeCounterpartyMOVE.json deleted file mode 100644 index bb11c3b16..000000000 --- a/protocol-units/bridge/service/abis/AtomicBridgeCounterpartyMOVE.json +++ /dev/null @@ -1 +0,0 @@ -{"abi":[{"type":"function","name":"abortBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"atomicBridgeInitiatorMOVE","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract AtomicBridgeInitiatorMOVE"}],"stateMutability":"view"},{"type":"function","name":"bridgeTransfers","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"originator","type":"bytes32","internalType":"bytes32"},{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"hashLock","type":"bytes32","internalType":"bytes32"},{"name":"timeLock","type":"uint256","internalType":"uint256"},{"name":"state","type":"uint8","internalType":"enum AtomicBridgeCounterpartyMOVE.MessageState"}],"stateMutability":"view"},{"type":"function","name":"completeBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"preImage","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"counterpartyTimeLockDuration","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_atomicBridgeInitiator","type":"address","internalType":"address"},{"name":"owner","type":"address","internalType":"address"},{"name":"_timeLockDuration","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"lockBridgeTransfer","inputs":[{"name":"originator","type":"bytes32","internalType":"bytes32"},{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"hashLock","type":"bytes32","internalType":"bytes32"},{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setAtomicBridgeInitiator","inputs":[{"name":"_atomicBridgeInitiator","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setTimeLockDuration","inputs":[{"name":"_timeLockDuration","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"BridgeTransferAborted","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"BridgeTransferCompleted","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"pre_image","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"BridgeTransferLocked","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"recipient","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"hashLock","type":"bytes32","indexed":false,"internalType":"bytes32"},{"name":"timeLock","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"BridgeTransferHasBeenCompleted","inputs":[]},{"type":"error","name":"BridgeTransferInvalid","inputs":[]},{"type":"error","name":"BridgeTransferStateNotInitialized","inputs":[]},{"type":"error","name":"BridgeTransferStateNotPending","inputs":[]},{"type":"error","name":"InsufficientMOVEBalance","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidSecret","inputs":[]},{"type":"error","name":"MOVETransferFailed","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]},{"type":"error","name":"TimeLockExpired","inputs":[]},{"type":"error","name":"TimeLockNotExpired","inputs":[]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroAmount","inputs":[]}],"bytecode":{"object":"0x6080604052348015600e575f80fd5b50610a1a8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100b1575f3560e01c80638da5cb5b1161006e5780638da5cb5b1461013a57806396d17d491461016a5780639f8f879914610181578063c95b659f14610194578063e0d9cbc4146101a7578063f2fde38b14610209575f80fd5b80631794bb3c146100b557806327b3ea07146100ca5780633b9b4640146100dd57806371115eb21461010c578063715018a61461011f5780637ce0874814610127575b5f80fd5b6100c86100c336600461087f565b61021c565b005b6100c86100d83660046108b9565b610372565b5f546100ef906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100c861011a3660046108fc565b6104d3565b6100c86104e0565b6100c8610135366004610913565b6104f3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166100ef565b61017360025481565b604051908152602001610103565b6100c861018f366004610933565b610543565b6100c86101a23660046108fc565b610647565b6101f76101b53660046108fc565b600160208190525f91825260409091208054918101546002820154600383015460048401546005909401546001600160a01b0390931693919290919060ff1686565b60405161010396959493929190610967565b6100c8610217366004610913565b6106f5565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156102615750825b90505f8267ffffffffffffffff16600114801561027d5750303b155b90508115801561028b575080155b156102a95760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156102d357845460ff60401b1916600160401b1785555b6001600160a01b0388166102fa5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038a1617905561031d87610737565b6002869055831561036857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b61037a610748565b805f0361039a57604051631f2a200560e01b815260040160405180910390fd5b5f600254426103a991906109bf565b90506040518060c00160405280878152602001846001600160a01b031681526020018381526020018581526020018281526020015f60028111156103ef576103ef610953565b90525f86815260016020818152604092839020845181559084015181830180546001600160a01b0319166001600160a01b0390921691909117905591830151600280840191909155606084015160038401556080840151600484015560a08401516005840180549193909260ff1990921691849081111561047257610472610953565b0217905550506002546040805185815260208101889052908101919091526001600160a01b038516915086907fa03230f5967404ba170c5be8445486911e87267000597e4ba9ed9d4b014fa4bd9060600160405180910390a3505050505050565b6104db610748565b600255565b6104e8610748565b6104f15f6107a3565b565b6104fb610748565b6001600160a01b0381166105225760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f82815260016020526040812090600582015460ff16600281111561056a5761056a610953565b146105885760405163a92c04c760e01b815260040160405180910390fd5b5f8260405160200161059c91815260200190565b604051602081830303815290604052805190602001209050816003015481146105d85760405163abab6bd760e01b815260040160405180910390fd5b81600401544211156105fd5760405163179a39d160e01b815260040160405180910390fd5b60058201805460ff1916600117905560405183815284907f05ddc886acde01b77731bfad1dcfb6abf529f05c28ea66556fe87429bb2789ea9060200160405180910390a250505050565b61064f610748565b5f81815260016020526040812090600582015460ff16600281111561067657610676610953565b146106945760405163a92c04c760e01b815260040160405180910390fd5b806004015442116106b85760405163191f4d1b60e31b815260040160405180910390fd5b60058101805460ff1916600217905560405182907f9b398e0a546c4aa218ac0b98f5e2196a7aff605b50c19eb795f9cb3b2f5d5b55905f90a25050565b6106fd610748565b6001600160a01b03811661072b57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610734816107a3565b50565b61073f610813565b6107348161085c565b3361077a7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146104f15760405163118cdaa760e01b8152336004820152602401610722565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166104f157604051631afcd79f60e31b815260040160405180910390fd5b6106fd610813565b80356001600160a01b038116811461087a575f80fd5b919050565b5f805f60608486031215610891575f80fd5b61089a84610864565b92506108a860208501610864565b929592945050506040919091013590565b5f805f805f60a086880312156108cd575f80fd5b8535945060208601359350604086013592506108eb60608701610864565b949793965091946080013592915050565b5f6020828403121561090c575f80fd5b5035919050565b5f60208284031215610923575f80fd5b61092c82610864565b9392505050565b5f8060408385031215610944575f80fd5b50508035926020909101359150565b634e487b7160e01b5f52602160045260245ffd5b8681526001600160a01b038616602082015260408101859052606081018490526080810183905260c08101600383106109ae57634e487b7160e01b5f52602160045260245ffd5b8260a0830152979650505050505050565b808201808211156109de57634e487b7160e01b5f52601160045260245ffd5b9291505056fea26469706673582212201cf10841a18016e56cd91bcf2a5fb41a20b61d49418de88247d723340792edf864736f6c634300081a0033","sourceMap":"319:3265:42:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561000f575f80fd5b50600436106100b1575f3560e01c80638da5cb5b1161006e5780638da5cb5b1461013a57806396d17d491461016a5780639f8f879914610181578063c95b659f14610194578063e0d9cbc4146101a7578063f2fde38b14610209575f80fd5b80631794bb3c146100b557806327b3ea07146100ca5780633b9b4640146100dd57806371115eb21461010c578063715018a61461011f5780637ce0874814610127575b5f80fd5b6100c86100c336600461087f565b61021c565b005b6100c86100d83660046108b9565b610372565b5f546100ef906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100c861011a3660046108fc565b6104d3565b6100c86104e0565b6100c8610135366004610913565b6104f3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166100ef565b61017360025481565b604051908152602001610103565b6100c861018f366004610933565b610543565b6100c86101a23660046108fc565b610647565b6101f76101b53660046108fc565b600160208190525f91825260409091208054918101546002820154600383015460048401546005909401546001600160a01b0390931693919290919060ff1686565b60405161010396959493929190610967565b6100c8610217366004610913565b6106f5565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156102615750825b90505f8267ffffffffffffffff16600114801561027d5750303b155b90508115801561028b575080155b156102a95760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156102d357845460ff60401b1916600160401b1785555b6001600160a01b0388166102fa5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038a1617905561031d87610737565b6002869055831561036857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b61037a610748565b805f0361039a57604051631f2a200560e01b815260040160405180910390fd5b5f600254426103a991906109bf565b90506040518060c00160405280878152602001846001600160a01b031681526020018381526020018581526020018281526020015f60028111156103ef576103ef610953565b90525f86815260016020818152604092839020845181559084015181830180546001600160a01b0319166001600160a01b0390921691909117905591830151600280840191909155606084015160038401556080840151600484015560a08401516005840180549193909260ff1990921691849081111561047257610472610953565b0217905550506002546040805185815260208101889052908101919091526001600160a01b038516915086907fa03230f5967404ba170c5be8445486911e87267000597e4ba9ed9d4b014fa4bd9060600160405180910390a3505050505050565b6104db610748565b600255565b6104e8610748565b6104f15f6107a3565b565b6104fb610748565b6001600160a01b0381166105225760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f82815260016020526040812090600582015460ff16600281111561056a5761056a610953565b146105885760405163a92c04c760e01b815260040160405180910390fd5b5f8260405160200161059c91815260200190565b604051602081830303815290604052805190602001209050816003015481146105d85760405163abab6bd760e01b815260040160405180910390fd5b81600401544211156105fd5760405163179a39d160e01b815260040160405180910390fd5b60058201805460ff1916600117905560405183815284907f05ddc886acde01b77731bfad1dcfb6abf529f05c28ea66556fe87429bb2789ea9060200160405180910390a250505050565b61064f610748565b5f81815260016020526040812090600582015460ff16600281111561067657610676610953565b146106945760405163a92c04c760e01b815260040160405180910390fd5b806004015442116106b85760405163191f4d1b60e31b815260040160405180910390fd5b60058101805460ff1916600217905560405182907f9b398e0a546c4aa218ac0b98f5e2196a7aff605b50c19eb795f9cb3b2f5d5b55905f90a25050565b6106fd610748565b6001600160a01b03811661072b57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610734816107a3565b50565b61073f610813565b6107348161085c565b3361077a7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146104f15760405163118cdaa760e01b8152336004820152602401610722565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166104f157604051631afcd79f60e31b815260040160405180910390fd5b6106fd610813565b80356001600160a01b038116811461087a575f80fd5b919050565b5f805f60608486031215610891575f80fd5b61089a84610864565b92506108a860208501610864565b929592945050506040919091013590565b5f805f805f60a086880312156108cd575f80fd5b8535945060208601359350604086013592506108eb60608701610864565b949793965091946080013592915050565b5f6020828403121561090c575f80fd5b5035919050565b5f60208284031215610923575f80fd5b61092c82610864565b9392505050565b5f8060408385031215610944575f80fd5b50508035926020909101359150565b634e487b7160e01b5f52602160045260245ffd5b8681526001600160a01b038616602082015260408101859052606081018490526080810183905260c08101600383106109ae57634e487b7160e01b5f52602160045260245ffd5b8260a0830152979650505050505050565b808201808211156109de57634e487b7160e01b5f52601160045260245ffd5b9291505056fea26469706673582212201cf10841a18016e56cd91bcf2a5fb41a20b61d49418de88247d723340792edf864736f6c634300081a0033","sourceMap":"319:3265:42:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;925:420;;;;;;:::i;:::-;;:::i;:::-;;1754:784;;;;;;:::i;:::-;;:::i;701:58::-;;;;;-1:-1:-1;;;;;701:58:42;;;;;;-1:-1:-1;;;;;1437:32:49;;;1419:51;;1407:2;1392:18;701:58:42;;;;;;;;1608:140;;;;;;:::i;:::-;;:::i;3155:101:23:-;;;:::i;1351:251:42:-;;;;;;:::i;:::-;;:::i;2441:144:23:-;1313:22;2570:8;-1:-1:-1;;;;;2570:8:23;2441:144;;875:43:42;;;;;;;;;2257:25:49;;;2245:2;2230:18;875:43:42;2111:177:49;2544:596:42;;;;;;:::i;:::-;;:::i;3146:436::-;;;;;;:::i;:::-;;:::i;765:64::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;765:64:42;;;;;;;;;;;;;;;;;;;;;;;;;:::i;3405:215:23:-;;;;;;:::i;:::-;;:::i;925:420:42:-;8870:21:24;4302:15;;-1:-1:-1;;;4302:15:24;;;;4301:16;;4348:14;;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;:16;;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:24;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:24;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:24;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:24;-1:-1:-1;;;5013:22:24;;;4979:67;-1:-1:-1;;;;;1052:36:42;::::1;1048:62;;1097:13;;-1:-1:-1::0;;;1097:13:42::1;;;;;;;;;;;1048:62;1120:25;:77:::0;;-1:-1:-1;;;;;;1120:77:42::1;-1:-1:-1::0;;;;;1120:77:42;::::1;;::::0;;1207:21:::1;1222:5:::0;1207:14:::1;:21::i;:::-;1290:28;:48:::0;;;5066:101:24;;;;5100:23;;-1:-1:-1;;;;5100:23:24;;;5142:14;;-1:-1:-1;3895:50:49;;5142:14:24;;3883:2:49;3868:18;5142:14:24;;;;;;;5066:101;4092:1081;;;;;925:420:42;;;:::o;1754:784::-;2334:13:23;:11;:13::i;:::-;1960:6:42::1;1970:1;1960:11:::0;1956:36:::1;;1980:12;;-1:-1:-1::0;;;1980:12:42::1;;;;;;;;;;;1956:36;2070:16;2107:28;;2089:15;:46;;;;:::i;:::-;2070:65;;2182:236;;;;;;;;2264:10;2182:236;;;;2229:9;-1:-1:-1::0;;;;;2182:236:42::1;;;;;2296:6;2182:236;;;;2326:8;2182:236;;;;2358:8;2182:236;;;;2387:20;2182:236;;;;;;;;:::i;:::-;::::0;;2146:33:::1;::::0;;;:15:::1;:33;::::0;;;;;;;;:272;;;;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;;;;;2146:272:42::1;-1:-1:-1::0;;;;;2146:272:42;;::::1;::::0;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;::::1;::::0;;;;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;;;;;;-1:-1:-1;;2146:272:42;;::::1;::::0;;;;::::1;;;;;;:::i;:::-;;;::::0;;-1:-1:-1;;2502:28:42::1;::::0;2434:97:::1;::::0;;4385:25:49;;;4441:2;4426:18;;4419:34;;;4469:18;;;4462:34;;;;-1:-1:-1;;;;;2434:97:42;::::1;::::0;-1:-1:-1;2455:16:42;;2434:97:::1;::::0;4373:2:49;4358:18;2434:97:42::1;;;;;;;1946:592;1754:784:::0;;;;;:::o;1608:140::-;2334:13:23;:11;:13::i;:::-;1693:28:42::1;:48:::0;1608:140::o;3155:101:23:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;:::-;3155:101::o:0;1351:251:42:-;2334:13:23;:11;:13::i;:::-;-1:-1:-1;;;;;1450:36:42;::::1;1446:62;;1495:13;;-1:-1:-1::0;;;1495:13:42::1;;;;;;;;;;;1446:62;1518:25;:77:::0;;-1:-1:-1;;;;;;1518:77:42::1;-1:-1:-1::0;;;;;1518:77:42;;;::::1;::::0;;;::::1;::::0;;1351:251::o;2544:596::-;2639:37;2679:33;;;:15;:33;;;;;;2726:13;;;;;;:37;;;;;;;;:::i;:::-;;2722:81;;2772:31;;-1:-1:-1;;;2772:31:42;;;;;;;;;;;2722:81;2813:20;2863:8;2846:26;;;;;;4636:19:49;;4680:2;4671:12;;4507:182;2846:26:42;;;;;;;;;;;;;2836:37;;;;;;2813:60;;2903:7;:16;;;2887:12;:32;2883:60;;2928:15;;-1:-1:-1;;;2928:15:42;;;;;;;;;;;2883:60;2975:7;:16;;;2957:15;:34;2953:64;;;3000:17;;-1:-1:-1;;;3000:17:42;;;;;;;;;;;2953:64;3028:13;;;:38;;-1:-1:-1;;3028:38:42;3044:22;3028:38;;;3082:51;;2257:25:49;;;3106:16:42;;3082:51;;2245:2:49;2230:18;3082:51:42;;;;;;;2629:511;;2544:596;;:::o;3146:436::-;2334:13:23;:11;:13::i;:::-;3230:37:42::1;3270:33:::0;;;:15:::1;:33;::::0;;;;;3317:13:::1;::::0;::::1;::::0;::::1;;:37;::::0;::::1;;;;;;:::i;:::-;;3313:81;;3363:31;;-1:-1:-1::0;;;3363:31:42::1;;;;;;;;;;;3313:81;3427:7;:16;;;3408:15;:35;3404:68;;3452:20;;-1:-1:-1::0;;;3452:20:42::1;;;;;;;;;;;3404:68;3483:13;::::0;::::1;:37:::0;;-1:-1:-1;;3483:37:42::1;3499:21;3483:37;::::0;;3536:39:::1;::::0;3558:16;;3536:39:::1;::::0;-1:-1:-1;;3536:39:42::1;3220:362;3146:436:::0;:::o;3405:215:23:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:23;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:23;;3562:1:::1;3534:31;::::0;::::1;1419:51:49::0;1392:18;;3534:31:23::1;;;;;;;;3485:91;3585:28;3604:8;3585:18;:28::i;:::-;3405:215:::0;:::o;1847:127::-;6931:20:24;:18;:20::i;:::-;1929:38:23::1;1954:12;1929:24;:38::i;2658:162::-:0;966:10:26;2717:7:23;1313:22;2570:8;-1:-1:-1;;;;;2570:8:23;;2441:144;2717:7;-1:-1:-1;;;;;2717:23:23;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:23;;966:10:26;2763:40:23;;;1419:51:49;1392:18;;2763:40:23;1238:238:49;3774:248:23;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:23;;-1:-1:-1;;;;;3941:19:23;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;7084:141:24:-;8870:21;8560:40;-1:-1:-1;;;8560:40:24;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:24;;;;;;;;;;;1980:235:23;6931:20:24;:18;:20::i;14:173:49:-;82:20;;-1:-1:-1;;;;;131:31:49;;121:42;;111:70;;177:1;174;167:12;111:70;14:173;;;:::o;192:374::-;269:6;277;285;338:2;326:9;317:7;313:23;309:32;306:52;;;354:1;351;344:12;306:52;377:29;396:9;377:29;:::i;:::-;367:39;;425:38;459:2;448:9;444:18;425:38;:::i;:::-;192:374;;415:48;;-1:-1:-1;;;532:2:49;517:18;;;;504:32;;192:374::o;571:662::-;666:6;674;682;690;698;751:3;739:9;730:7;726:23;722:33;719:53;;;768:1;765;758:12;719:53;813:23;;;-1:-1:-1;933:2:49;918:18;;905:32;;-1:-1:-1;1036:2:49;1021:18;;1008:32;;-1:-1:-1;1085:38:49;1119:2;1104:18;;1085:38;:::i;:::-;571:662;;;;-1:-1:-1;571:662:49;;1196:3;1181:19;1168:33;;571:662;-1:-1:-1;;571:662:49:o;1481:226::-;1540:6;1593:2;1581:9;1572:7;1568:23;1564:32;1561:52;;;1609:1;1606;1599:12;1561:52;-1:-1:-1;1654:23:49;;1481:226;-1:-1:-1;1481:226:49:o;1712:186::-;1771:6;1824:2;1812:9;1803:7;1799:23;1795:32;1792:52;;;1840:1;1837;1830:12;1792:52;1863:29;1882:9;1863:29;:::i;:::-;1853:39;1712:186;-1:-1:-1;;;1712:186:49:o;2293:346::-;2361:6;2369;2422:2;2410:9;2401:7;2397:23;2393:32;2390:52;;;2438:1;2435;2428:12;2390:52;-1:-1:-1;;2483:23:49;;;2603:2;2588:18;;;2575:32;;-1:-1:-1;2293:346:49:o;2875:127::-;2936:10;2931:3;2927:20;2924:1;2917:31;2967:4;2964:1;2957:15;2991:4;2988:1;2981:15;3007:730;3310:25;;;-1:-1:-1;;;;;3371:32:49;;3366:2;3351:18;;3344:60;3435:2;3420:18;;3413:34;;;3478:2;3463:18;;3456:34;;;3521:3;3506:19;;3499:35;;;3297:3;3282:19;;3564:1;3553:13;;3543:144;;3609:10;3604:3;3600:20;3597:1;3590:31;3644:4;3641:1;3634:15;3672:4;3669:1;3662:15;3543:144;3724:6;3718:3;3707:9;3703:19;3696:35;3007:730;;;;;;;;;:::o;3956:222::-;4021:9;;;4042:10;;;4039:133;;;4094:10;4089:3;4085:20;4082:1;4075:31;4129:4;4126:1;4119:15;4157:4;4154:1;4147:15;4039:133;3956:222;;;;:::o","linkReferences":{}},"methodIdentifiers":{"abortBridgeTransfer(bytes32)":"c95b659f","atomicBridgeInitiatorMOVE()":"3b9b4640","bridgeTransfers(bytes32)":"e0d9cbc4","completeBridgeTransfer(bytes32,bytes32)":"9f8f8799","counterpartyTimeLockDuration()":"96d17d49","initialize(address,address,uint256)":"1794bb3c","lockBridgeTransfer(bytes32,bytes32,bytes32,address,uint256)":"27b3ea07","owner()":"8da5cb5b","renounceOwnership()":"715018a6","setAtomicBridgeInitiator(address)":"7ce08748","setTimeLockDuration(uint256)":"71115eb2","transferOwnership(address)":"f2fde38b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"BridgeTransferHasBeenCompleted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BridgeTransferInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BridgeTransferStateNotInitialized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BridgeTransferStateNotPending\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientMOVEBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSecret\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MOVETransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeLockExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeLockNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAmount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"BridgeTransferAborted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pre_image\",\"type\":\"bytes32\"}],\"name\":\"BridgeTransferCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hashLock\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeLock\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"abortBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"atomicBridgeInitiatorMOVE\",\"outputs\":[{\"internalType\":\"contract AtomicBridgeInitiatorMOVE\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"bridgeTransfers\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"originator\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"hashLock\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"timeLock\",\"type\":\"uint256\"},{\"internalType\":\"enum AtomicBridgeCounterpartyMOVE.MessageState\",\"name\":\"state\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"preImage\",\"type\":\"bytes32\"}],\"name\":\"completeBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counterpartyTimeLockDuration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_atomicBridgeInitiator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_timeLockDuration\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originator\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"hashLock\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"lockBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_atomicBridgeInitiator\",\"type\":\"address\"}],\"name\":\"setAtomicBridgeInitiator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_timeLockDuration\",\"type\":\"uint256\"}],\"name\":\"setTimeLockDuration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}],\"OwnableInvalidOwner(address)\":[{\"details\":\"The owner is not a valid owner account. (eg. `address(0)`)\"}],\"OwnableUnauthorizedAccount(address)\":[{\"details\":\"The caller account is not authorized to perform an operation.\"}]},\"events\":{\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"}},\"kind\":\"dev\",\"methods\":{\"abortBridgeTransfer(bytes32)\":{\"details\":\"Cancels the bridge transfer and refunds the initiator if the timelock has expired\",\"params\":{\"bridgeTransferId\":\"Unique identifier for the BridgeTransfer\"}},\"completeBridgeTransfer(bytes32,bytes32)\":{\"details\":\"Completes the bridge transfer and withdraws WETH to the recipient\",\"params\":{\"bridgeTransferId\":\"Unique identifier for the BridgeTransfer\",\"preImage\":\"The secret that unlocks the funds\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/AtomicBridgeCounterpartyMOVE.sol\":\"AtomicBridgeCounterpartyMOVE\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol\":{\"keccak256\":\"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6\",\"dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol\":{\"keccak256\":\"0xbb96dc9c468170c3224126e953de917e06332ec5909a3d85e6e5bb0df10c5139\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d14e6486e127e7e31c2ffccfc212c7ebaaecf8fb05677575128b449ee113def2\",\"dweb:/ipfs/QmabvyfStwBcum8mGfkmxcTV45rjyHmzHGCxfxyhmu48Yx\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol\":{\"keccak256\":\"0x880da465c203cec76b10d72dbd87c80f387df4102274f23eea1f9c9b0918792b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://399594cd8bb0143bc9e55e0f1d071d0d8c850a394fb7a319d50edd55d9ed822b\",\"dweb:/ipfs/QmbPZzgtT6LEm9CMqWfagQFwETbV1ztpECBB1DtQHrKiRz\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xe06a3f08a987af6ad2e1c1e774405d4fe08f1694b67517438b467cecf0da0ef7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df6f0c459663c9858b6cba2cda1d14a7d05a985bed6d2de72bd8e78c25ee79db\",\"dweb:/ipfs/QmeTTxZ7qVk9rjEv2R4CpCwdf8UMCcRqDNMvzNxHc3Fnn9\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"keccak256\":\"0x70f2f713b13b7ce4610bcd0ac9fec0f3cc43693b043abcb8dc40a42a726eb330\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c13d13304ac79a83ab1c30168967d19e2203342ebbd6a9bbce4db7550522dcbf\",\"dweb:/ipfs/QmeN5jKMN2vw5bhacr6tkg78afbTTZUeaacNHqjWt4Ew1r\"]},\"src/AtomicBridgeCounterpartyMOVE.sol\":{\"keccak256\":\"0xb3885e04fe9d8a19b4a36a2c4976e3f3ca044d16addcfe8267f2ed4dbb9d785d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9a73ecee45f2dbc4a26c722b8d4ab7fde39d262ba628d0c899aa79e3c7d0dbb6\",\"dweb:/ipfs/QmdsTYt1JJjTXPA8Ykym3TFH7br3qcbqiwkKUwUF8apvrg\"]},\"src/AtomicBridgeInitiatorMOVE.sol\":{\"keccak256\":\"0x5b19ecc44068863e3cb0727feb40cf40e2704ad78c1cbcc46943e3dec0c2d276\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0489c79d358a014df14338776376728f35be92bc9d6338eae228077db567dc7c\",\"dweb:/ipfs/QmXqVmMCHqZkSe41LSpoxoEiNRP7UJp5qU6bN3UykCQRzE\"]},\"src/IAtomicBridgeCounterpartyMOVE.sol\":{\"keccak256\":\"0x6c7993557da48f7178593831ebe14d6ecc4d8e9a08172616d293428954a66a29\",\"urls\":[\"bzz-raw://287fb06824eaeb5619d7f2e016803463cc4f6fd7fdbfa1264308ea8d8168a9d6\",\"dweb:/ipfs/QmZUUjwLw48e9XKTVibqjC9mciNAezA2qUVsX2SRiunpoi\"]},\"src/IAtomicBridgeInitiatorMOVE.sol\":{\"keccak256\":\"0x3705cbaaf8910b786e5cb99d8d1bfa3f591add9a1d835703a69d6a77020ad841\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://12cca43905283adf49be8a23853952b90be09d3ba38b583fd8c2821e97ad8c0a\",\"dweb:/ipfs/QmaxHt6YUKb97dCz1rUjvjfM3XLcoLNvoFrUkDA6a77ER5\"]},\"src/MockMOVEToken.sol\":{\"keccak256\":\"0x71768bf12ff974ee8589bee8b951fa63c7db1f74f251c5997232643bc749a8e4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://38474add7fec1c814adf1ad614e4ca233b9f789f279690c698a3a94334c029cf\",\"dweb:/ipfs/Qmah2K8YEmLqnKjXcGvbSD55sm61zkKVvNMhehmrj6VzBs\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.26+commit.8a97fa7a"},"language":"Solidity","output":{"abi":[{"inputs":[],"type":"error","name":"BridgeTransferHasBeenCompleted"},{"inputs":[],"type":"error","name":"BridgeTransferInvalid"},{"inputs":[],"type":"error","name":"BridgeTransferStateNotInitialized"},{"inputs":[],"type":"error","name":"BridgeTransferStateNotPending"},{"inputs":[],"type":"error","name":"InsufficientMOVEBalance"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"InvalidSecret"},{"inputs":[],"type":"error","name":"MOVETransferFailed"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"type":"error","name":"OwnableInvalidOwner"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"type":"error","name":"OwnableUnauthorizedAccount"},{"inputs":[],"type":"error","name":"TimeLockExpired"},{"inputs":[],"type":"error","name":"TimeLockNotExpired"},{"inputs":[],"type":"error","name":"Unauthorized"},{"inputs":[],"type":"error","name":"ZeroAddress"},{"inputs":[],"type":"error","name":"ZeroAmount"},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true}],"type":"event","name":"BridgeTransferAborted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"pre_image","type":"bytes32","indexed":false}],"type":"event","name":"BridgeTransferCompleted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"address","name":"recipient","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"bytes32","name":"hashLock","type":"bytes32","indexed":false},{"internalType":"uint256","name":"timeLock","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferLocked","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"previousOwner","type":"address","indexed":true},{"internalType":"address","name":"newOwner","type":"address","indexed":true}],"type":"event","name":"OwnershipTransferred","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"abortBridgeTransfer"},{"inputs":[],"stateMutability":"view","type":"function","name":"atomicBridgeInitiatorMOVE","outputs":[{"internalType":"contract AtomicBridgeInitiatorMOVE","name":"","type":"address"}]},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"bridgeTransfers","outputs":[{"internalType":"bytes32","name":"originator","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"internalType":"uint256","name":"timeLock","type":"uint256"},{"internalType":"enum AtomicBridgeCounterpartyMOVE.MessageState","name":"state","type":"uint8"}]},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"bytes32","name":"preImage","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"completeBridgeTransfer"},{"inputs":[],"stateMutability":"view","type":"function","name":"counterpartyTimeLockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"_atomicBridgeInitiator","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"_timeLockDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"initialize"},{"inputs":[{"internalType":"bytes32","name":"originator","type":"bytes32"},{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"lockBridgeTransfer"},{"inputs":[],"stateMutability":"view","type":"function","name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"renounceOwnership"},{"inputs":[{"internalType":"address","name":"_atomicBridgeInitiator","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setAtomicBridgeInitiator"},{"inputs":[{"internalType":"uint256","name":"_timeLockDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"setTimeLockDuration"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"transferOwnership"}],"devdoc":{"kind":"dev","methods":{"abortBridgeTransfer(bytes32)":{"details":"Cancels the bridge transfer and refunds the initiator if the timelock has expired","params":{"bridgeTransferId":"Unique identifier for the BridgeTransfer"}},"completeBridgeTransfer(bytes32,bytes32)":{"details":"Completes the bridge transfer and withdraws WETH to the recipient","params":{"bridgeTransferId":"Unique identifier for the BridgeTransfer","preImage":"The secret that unlocks the funds"}},"owner()":{"details":"Returns the address of the current owner."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner."},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/AtomicBridgeCounterpartyMOVE.sol":"AtomicBridgeCounterpartyMOVE"},"evmVersion":"cancun","libraries":{}},"sources":{"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol":{"keccak256":"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a","urls":["bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6","dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol":{"keccak256":"0xbb96dc9c468170c3224126e953de917e06332ec5909a3d85e6e5bb0df10c5139","urls":["bzz-raw://d14e6486e127e7e31c2ffccfc212c7ebaaecf8fb05677575128b449ee113def2","dweb:/ipfs/QmabvyfStwBcum8mGfkmxcTV45rjyHmzHGCxfxyhmu48Yx"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol":{"keccak256":"0x880da465c203cec76b10d72dbd87c80f387df4102274f23eea1f9c9b0918792b","urls":["bzz-raw://399594cd8bb0143bc9e55e0f1d071d0d8c850a394fb7a319d50edd55d9ed822b","dweb:/ipfs/QmbPZzgtT6LEm9CMqWfagQFwETbV1ztpECBB1DtQHrKiRz"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0xe06a3f08a987af6ad2e1c1e774405d4fe08f1694b67517438b467cecf0da0ef7","urls":["bzz-raw://df6f0c459663c9858b6cba2cda1d14a7d05a985bed6d2de72bd8e78c25ee79db","dweb:/ipfs/QmeTTxZ7qVk9rjEv2R4CpCwdf8UMCcRqDNMvzNxHc3Fnn9"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol":{"keccak256":"0x70f2f713b13b7ce4610bcd0ac9fec0f3cc43693b043abcb8dc40a42a726eb330","urls":["bzz-raw://c13d13304ac79a83ab1c30168967d19e2203342ebbd6a9bbce4db7550522dcbf","dweb:/ipfs/QmeN5jKMN2vw5bhacr6tkg78afbTTZUeaacNHqjWt4Ew1r"],"license":"MIT"},"src/AtomicBridgeCounterpartyMOVE.sol":{"keccak256":"0xb3885e04fe9d8a19b4a36a2c4976e3f3ca044d16addcfe8267f2ed4dbb9d785d","urls":["bzz-raw://9a73ecee45f2dbc4a26c722b8d4ab7fde39d262ba628d0c899aa79e3c7d0dbb6","dweb:/ipfs/QmdsTYt1JJjTXPA8Ykym3TFH7br3qcbqiwkKUwUF8apvrg"],"license":"MIT"},"src/AtomicBridgeInitiatorMOVE.sol":{"keccak256":"0x5b19ecc44068863e3cb0727feb40cf40e2704ad78c1cbcc46943e3dec0c2d276","urls":["bzz-raw://0489c79d358a014df14338776376728f35be92bc9d6338eae228077db567dc7c","dweb:/ipfs/QmXqVmMCHqZkSe41LSpoxoEiNRP7UJp5qU6bN3UykCQRzE"],"license":"MIT"},"src/IAtomicBridgeCounterpartyMOVE.sol":{"keccak256":"0x6c7993557da48f7178593831ebe14d6ecc4d8e9a08172616d293428954a66a29","urls":["bzz-raw://287fb06824eaeb5619d7f2e016803463cc4f6fd7fdbfa1264308ea8d8168a9d6","dweb:/ipfs/QmZUUjwLw48e9XKTVibqjC9mciNAezA2qUVsX2SRiunpoi"],"license":null},"src/IAtomicBridgeInitiatorMOVE.sol":{"keccak256":"0x3705cbaaf8910b786e5cb99d8d1bfa3f591add9a1d835703a69d6a77020ad841","urls":["bzz-raw://12cca43905283adf49be8a23853952b90be09d3ba38b583fd8c2821e97ad8c0a","dweb:/ipfs/QmaxHt6YUKb97dCz1rUjvjfM3XLcoLNvoFrUkDA6a77ER5"],"license":"MIT"},"src/MockMOVEToken.sol":{"keccak256":"0x71768bf12ff974ee8589bee8b951fa63c7db1f74f251c5997232643bc749a8e4","urls":["bzz-raw://38474add7fec1c814adf1ad614e4ca233b9f789f279690c698a3a94334c029cf","dweb:/ipfs/Qmah2K8YEmLqnKjXcGvbSD55sm61zkKVvNMhehmrj6VzBs"],"license":"MIT"}},"version":1},"id":42} \ No newline at end of file diff --git a/protocol-units/bridge/service/abis/AtomicBridgeInitiatorMOVE.json b/protocol-units/bridge/service/abis/AtomicBridgeInitiatorMOVE.json deleted file mode 100644 index 08998b03a..000000000 --- a/protocol-units/bridge/service/abis/AtomicBridgeInitiatorMOVE.json +++ /dev/null @@ -1 +0,0 @@ -{"abi":[{"type":"function","name":"bridgeTransfers","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"originator","type":"address","internalType":"address"},{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"hashLock","type":"bytes32","internalType":"bytes32"},{"name":"timeLock","type":"uint256","internalType":"uint256"},{"name":"state","type":"uint8","internalType":"enum AtomicBridgeInitiatorMOVE.MessageState"}],"stateMutability":"view"},{"type":"function","name":"completeBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"preImage","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"counterpartyAddress","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_moveToken","type":"address","internalType":"address"},{"name":"owner","type":"address","internalType":"address"},{"name":"_timeLockDuration","type":"uint256","internalType":"uint256"},{"name":"_initialPoolBalance","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"initiateBridgeTransfer","inputs":[{"name":"moveAmount","type":"uint256","internalType":"uint256"},{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"hashLock","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"stateMutability":"nonpayable"},{"type":"function","name":"initiatorTimeLockDuration","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"moveToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ERC20Upgradeable"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"poolBalance","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"refundBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setCounterpartyAddress","inputs":[{"name":"_counterpartyAddress","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"withdrawMOVE","inputs":[{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"BridgeTransferCompleted","inputs":[{"name":"_bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"pre_image","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"BridgeTransferInitiated","inputs":[{"name":"_bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"_originator","type":"address","indexed":true,"internalType":"address"},{"name":"_recipient","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"_hashLock","type":"bytes32","indexed":false,"internalType":"bytes32"},{"name":"_timeLock","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BridgeTransferRefunded","inputs":[{"name":"_bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"BridgeTransferHasBeenCompleted","inputs":[]},{"type":"error","name":"BridgeTransferInvalid","inputs":[]},{"type":"error","name":"BridgeTransferStateNotInitialized","inputs":[]},{"type":"error","name":"InsufficientMOVEBalance","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidSecret","inputs":[]},{"type":"error","name":"MOVETransferFailed","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]},{"type":"error","name":"TimeLockNotExpired","inputs":[]},{"type":"error","name":"TimelockExpired","inputs":[]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroAmount","inputs":[]}],"bytecode":{"object":"0x6080604052348015600e575f80fd5b50610ecf8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100e4575f3560e01c806396365d4411610088578063e0d9cbc411610063578063e0d9cbc4146101d1578063e20c95ec14610232578063eb990c5914610245578063f2fde38b14610258575f80fd5b806396365d44146101a25780639f8f8799146101ab578063cacfb161146101be575f80fd5b80632b3948bb116100c35780632b3948bb1461014e5780635780802014610157578063715018a61461016a5780638da5cb5b14610172575f80fd5b80621a153e146100e85780631a8849a4146100fd5780631f92c08e14610123575b5f80fd5b6100fb6100f6366004610ccf565b61026b565b005b61011061010b366004610cef565b6102de565b6040519081526020015b60405180910390f35b600254610136906001600160a01b031681565b6040516001600160a01b03909116815260200161011a565b61011060055481565b6100fb610165366004610d18565b610580565b6100fb610740565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610136565b61011060015481565b6100fb6101b9366004610d2f565b610753565b6100fb6101cc366004610d4f565b6108c2565b6102206101df366004610d18565b5f6020819052908152604090208054600182015460028301546003840154600485015460059095015493946001600160a01b03909316939192909160ff1686565b60405161011a96959493929190610d8b565b600354610136906001600160a01b031681565b6100fb610253366004610de3565b6109ba565b6100fb610266366004610ccf565b610b4a565b610273610b87565b6001600160a01b0381166102bc5760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b60448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b5f338461031a5760405162461bcd60e51b815260206004820152600a60248201526916995c9bd05b5bdd5b9d60b21b60448201526064016102b3565b845f0361033a57604051631f2a200560e01b815260040160405180910390fd5b6003546040516323b872dd60e01b81526001600160a01b03838116600483015230602483015260448201889052909116906323b872dd906064016020604051808303815f875af1158015610390573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b49190610e22565b6103d157604051631a67cf2760e31b815260040160405180910390fd5b8460015f8282546103e29190610e55565b90915550506005546004805483928792879242915f61040083610e6e565b9091555060405160609690961b6bffffffffffffffffffffffff19166020870152603486019490945260548501929092526074840152609483015260b482015260d4016040516020818303038152906040528051906020012091506040518060c00160405280868152602001826001600160a01b03168152602001858152602001848152602001600554426104959190610e55565b81525f60209182018190528481528082526040908190208351815591830151600180840180546001600160a01b0319166001600160a01b039093169290921790915590830151600280840191909155606084015160038401556080840151600484015560a08401516005840180549193909260ff1990921691849081111561051f5761051f610d77565b021790555050600554604080518881526020810187905280820192909252518692506001600160a01b0384169185917f44e287be4fbd3a2dcc143a376301094fd2f809dcc2a8d3c09d0a0715224766c49181900360600190a4509392505050565b5f81815260208190526040812090600582015460ff1660028111156105a7576105a7610d77565b146105fe5760405162461bcd60e51b815260206004820152602160248201527f4272696467655472616e7366657253746174654e6f74496e697469616c697a656044820152601960fa1b60648201526084016102b3565b80600401544210156106475760405162461bcd60e51b8152602060048201526012602482015271151a5b59531bd8dad39bdd115e1c1a5c995960721b60448201526064016102b3565b60058101805460ff191660021790556003546001820154825460405163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb906044016020604051808303815f875af11580156106ad573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106d19190610e22565b6107125760405162461bcd60e51b81526020600482015260126024820152711353d591551c985b9cd9995c91985a5b195960721b60448201526064016102b3565b60405182907f4fee0a65c921e50a9623c3abe10a4067e49c03ef491e7b406dace7cb79c12c61905f90a25050565b610748610b87565b6107515f610be2565b565b61075b610b87565b5f82815260208190526040812090600582015460ff16600281111561078257610782610d77565b146107cf5760405162461bcd60e51b815260206004820152601e60248201527f4272696467655472616e736665724861734265656e436f6d706c65746564000060448201526064016102b3565b600381015460408051602081018590520160405160208183030381529060405280519060200120146108335760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5914d958dc995d609a1b60448201526064016102b3565b80600401544211156108795760405162461bcd60e51b815260206004820152600f60248201526e151a5b595b1bd8dad15e1c1a5c9959608a1b60448201526064016102b3565b60058101805460ff1916600117905560405182815283907f05ddc886acde01b77731bfad1dcfb6abf529f05c28ea66556fe87429bb2789ea9060200160405180910390a2505050565b6002546001600160a01b031633146108ec576040516282b42960e81b815260040160405180910390fd5b80600154101561090f576040516381a6aee360e01b815260040160405180910390fd5b8060015f8282546109209190610e86565b909155505060035460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015610975573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109999190610e22565b6109b657604051631a67cf2760e31b815260040160405180910390fd5b5050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156109ff5750825b90505f8267ffffffffffffffff166001148015610a1b5750303b155b905081158015610a29575080155b15610a475760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610a7157845460ff60401b1916600160401b1785555b6001600160a01b03891615801590610a9157506001600160a01b03881615155b610acb5760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b60448201526064016102b3565b600380546001600160a01b0319166001600160a01b038b16179055610aef88610c52565b600587905560018690558315610b3f57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610b52610b87565b6001600160a01b038116610b7b57604051631e4fbdf760e01b81525f60048201526024016102b3565b610b8481610be2565b50565b33610bb97f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146107515760405163118cdaa760e01b81523360048201526024016102b3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b610c5a610c63565b610b8481610cac565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661075157604051631afcd79f60e31b815260040160405180910390fd5b610b52610c63565b80356001600160a01b0381168114610cca575f80fd5b919050565b5f60208284031215610cdf575f80fd5b610ce882610cb4565b9392505050565b5f805f60608486031215610d01575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215610d28575f80fd5b5035919050565b5f8060408385031215610d40575f80fd5b50508035926020909101359150565b5f8060408385031215610d60575f80fd5b610d6983610cb4565b946020939093013593505050565b634e487b7160e01b5f52602160045260245ffd5b8681526001600160a01b038616602082015260408101859052606081018490526080810183905260c0810160038310610dd257634e487b7160e01b5f52602160045260245ffd5b8260a0830152979650505050505050565b5f805f8060808587031215610df6575f80fd5b610dff85610cb4565b9350610e0d60208601610cb4565b93969395505050506040820135916060013590565b5f60208284031215610e32575f80fd5b81518015158114610ce8575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610e6857610e68610e41565b92915050565b5f60018201610e7f57610e7f610e41565b5060010190565b81810381811115610e6857610e68610e4156fea264697066735822122005f975a8a8fa8ee2ccdc98a7f0d922184b6276f4f8fd040834da1d1d34fa214d64736f6c634300081a0033","sourceMap":"392:4505:43:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561000f575f80fd5b50600436106100e4575f3560e01c806396365d4411610088578063e0d9cbc411610063578063e0d9cbc4146101d1578063e20c95ec14610232578063eb990c5914610245578063f2fde38b14610258575f80fd5b806396365d44146101a25780639f8f8799146101ab578063cacfb161146101be575f80fd5b80632b3948bb116100c35780632b3948bb1461014e5780635780802014610157578063715018a61461016a5780638da5cb5b14610172575f80fd5b80621a153e146100e85780631a8849a4146100fd5780631f92c08e14610123575b5f80fd5b6100fb6100f6366004610ccf565b61026b565b005b61011061010b366004610cef565b6102de565b6040519081526020015b60405180910390f35b600254610136906001600160a01b031681565b6040516001600160a01b03909116815260200161011a565b61011060055481565b6100fb610165366004610d18565b610580565b6100fb610740565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610136565b61011060015481565b6100fb6101b9366004610d2f565b610753565b6100fb6101cc366004610d4f565b6108c2565b6102206101df366004610d18565b5f6020819052908152604090208054600182015460028301546003840154600485015460059095015493946001600160a01b03909316939192909160ff1686565b60405161011a96959493929190610d8b565b600354610136906001600160a01b031681565b6100fb610253366004610de3565b6109ba565b6100fb610266366004610ccf565b610b4a565b610273610b87565b6001600160a01b0381166102bc5760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b60448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b5f338461031a5760405162461bcd60e51b815260206004820152600a60248201526916995c9bd05b5bdd5b9d60b21b60448201526064016102b3565b845f0361033a57604051631f2a200560e01b815260040160405180910390fd5b6003546040516323b872dd60e01b81526001600160a01b03838116600483015230602483015260448201889052909116906323b872dd906064016020604051808303815f875af1158015610390573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b49190610e22565b6103d157604051631a67cf2760e31b815260040160405180910390fd5b8460015f8282546103e29190610e55565b90915550506005546004805483928792879242915f61040083610e6e565b9091555060405160609690961b6bffffffffffffffffffffffff19166020870152603486019490945260548501929092526074840152609483015260b482015260d4016040516020818303038152906040528051906020012091506040518060c00160405280868152602001826001600160a01b03168152602001858152602001848152602001600554426104959190610e55565b81525f60209182018190528481528082526040908190208351815591830151600180840180546001600160a01b0319166001600160a01b039093169290921790915590830151600280840191909155606084015160038401556080840151600484015560a08401516005840180549193909260ff1990921691849081111561051f5761051f610d77565b021790555050600554604080518881526020810187905280820192909252518692506001600160a01b0384169185917f44e287be4fbd3a2dcc143a376301094fd2f809dcc2a8d3c09d0a0715224766c49181900360600190a4509392505050565b5f81815260208190526040812090600582015460ff1660028111156105a7576105a7610d77565b146105fe5760405162461bcd60e51b815260206004820152602160248201527f4272696467655472616e7366657253746174654e6f74496e697469616c697a656044820152601960fa1b60648201526084016102b3565b80600401544210156106475760405162461bcd60e51b8152602060048201526012602482015271151a5b59531bd8dad39bdd115e1c1a5c995960721b60448201526064016102b3565b60058101805460ff191660021790556003546001820154825460405163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb906044016020604051808303815f875af11580156106ad573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106d19190610e22565b6107125760405162461bcd60e51b81526020600482015260126024820152711353d591551c985b9cd9995c91985a5b195960721b60448201526064016102b3565b60405182907f4fee0a65c921e50a9623c3abe10a4067e49c03ef491e7b406dace7cb79c12c61905f90a25050565b610748610b87565b6107515f610be2565b565b61075b610b87565b5f82815260208190526040812090600582015460ff16600281111561078257610782610d77565b146107cf5760405162461bcd60e51b815260206004820152601e60248201527f4272696467655472616e736665724861734265656e436f6d706c65746564000060448201526064016102b3565b600381015460408051602081018590520160405160208183030381529060405280519060200120146108335760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5914d958dc995d609a1b60448201526064016102b3565b80600401544211156108795760405162461bcd60e51b815260206004820152600f60248201526e151a5b595b1bd8dad15e1c1a5c9959608a1b60448201526064016102b3565b60058101805460ff1916600117905560405182815283907f05ddc886acde01b77731bfad1dcfb6abf529f05c28ea66556fe87429bb2789ea9060200160405180910390a2505050565b6002546001600160a01b031633146108ec576040516282b42960e81b815260040160405180910390fd5b80600154101561090f576040516381a6aee360e01b815260040160405180910390fd5b8060015f8282546109209190610e86565b909155505060035460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015610975573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109999190610e22565b6109b657604051631a67cf2760e31b815260040160405180910390fd5b5050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156109ff5750825b90505f8267ffffffffffffffff166001148015610a1b5750303b155b905081158015610a29575080155b15610a475760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610a7157845460ff60401b1916600160401b1785555b6001600160a01b03891615801590610a9157506001600160a01b03881615155b610acb5760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b60448201526064016102b3565b600380546001600160a01b0319166001600160a01b038b16179055610aef88610c52565b600587905560018690558315610b3f57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610b52610b87565b6001600160a01b038116610b7b57604051631e4fbdf760e01b81525f60048201526024016102b3565b610b8481610be2565b50565b33610bb97f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146107515760405163118cdaa760e01b81523360048201526024016102b3565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b610c5a610c63565b610b8481610cac565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661075157604051631afcd79f60e31b815260040160405180910390fd5b610b52610c63565b80356001600160a01b0381168114610cca575f80fd5b919050565b5f60208284031215610cdf575f80fd5b610ce882610cb4565b9392505050565b5f805f60608486031215610d01575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215610d28575f80fd5b5035919050565b5f8060408385031215610d40575f80fd5b50508035926020909101359150565b5f8060408385031215610d60575f80fd5b610d6983610cb4565b946020939093013593505050565b634e487b7160e01b5f52602160045260245ffd5b8681526001600160a01b038616602082015260408101859052606081018490526080810183905260c0810160038310610dd257634e487b7160e01b5f52602160045260245ffd5b8260a0830152979650505050505050565b5f805f8060808587031215610df6575f80fd5b610dff85610cb4565b9350610e0d60208601610cb4565b93969395505050506040820135916060013590565b5f60208284031215610e32575f80fd5b81518015158114610ce8575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610e6857610e68610e41565b92915050565b5f60018201610e7f57610e7f610e41565b5060010190565b81810381811115610e6857610e68610e4156fea264697066735822122005f975a8a8fa8ee2ccdc98a7f0d922184b6276f4f8fd040834da1d1d34fa214d64736f6c634300081a0033","sourceMap":"392:4505:43:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1825:208;;;;;;:::i;:::-;;:::i;:::-;;2039:1358;;;;;;:::i;:::-;;:::i;:::-;;;1000:25:49;;;988:2;973:18;2039:1358:43;;;;;;;;989:34;;;;;-1:-1:-1;;;;;989:34:43;;;;;;-1:-1:-1;;;;;1200:32:49;;;1182:51;;1170:2;1155:18;989:34:43;1036:203:49;1135:40:43;;;;;;3999:567;;;;;;:::i;:::-;;:::i;3155:101:23:-;;;:::i;2441:144::-;1313:22;2570:8;-1:-1:-1;;;;;2570:8:23;2441:144;;956:26:43;;;;;;3403:590;;;;;;:::i;:::-;;:::i;4572:323::-;;;;;;:::i;:::-;;:::i;855:57::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;855:57:43;;;;;;;;;;;;;;;;;;;;;;;;:::i;1029:33::-;;;;;-1:-1:-1;;;;;1029:33:43;;;1297:522;;;;;;:::i;:::-;;:::i;3405:215:23:-;;;;;;:::i;:::-;;:::i;1825:208:43:-;2334:13:23;:11;:13::i;:::-;-1:-1:-1;;;;;1924:34:43;::::1;1916:58;;;::::0;-1:-1:-1;;;1916:58:43;;4116:2:49;1916:58:43::1;::::0;::::1;4098:21:49::0;4155:2;4135:18;;;4128:30;-1:-1:-1;;;4174:18:49;;;4167:41;4225:18;;1916:58:43::1;;;;;;;;;1984:19;:42:::0;;-1:-1:-1;;;;;;1984:42:43::1;-1:-1:-1::0;;;;;1984:42:43;;;::::1;::::0;;;::::1;::::0;;1825:208::o;2039:1358::-;2162:24;2223:10;2264:14;2256:37;;;;-1:-1:-1;;;2256:37:43;;4456:2:49;2256:37:43;;;4438:21:49;4495:2;4475:18;;;4468:30;-1:-1:-1;;;4514:18:49;;;4507:40;4564:18;;2256:37:43;4254:334:49;2256:37:43;2350:10;2364:1;2350:15;2346:65;;2388:12;;-1:-1:-1;;;2388:12:43;;;;;;;;;;;2346:65;2492:9;;:61;;-1:-1:-1;;;2492:61:43;;-1:-1:-1;;;;;4813:32:49;;;2492:61:43;;;4795:51:49;2535:4:43;4862:18:49;;;4855:60;4931:18;;;4924:34;;;2492:9:43;;;;:22;;4768:18:49;;2492:61:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2487:120;;2576:20;;-1:-1:-1;;;2576:20:43;;;;;;;;;;;2487:120;2667:10;2652:11;;:25;;;;;;;:::i;:::-;;;;-1:-1:-1;;2856:25:43;;2900:5;:7;;2823:10;;2835:9;;2846:8;;2883:15;;2900:5;:7;;;:::i;:::-;;;;-1:-1:-1;2806:102:43;;5942:2:49;5938:15;;;;-1:-1:-1;;5934:53:49;2806:102:43;;;5922:66:49;6004:12;;;5997:28;;;;6041:12;;;6034:28;;;;6078:12;;;6071:28;6115:13;;;6108:29;6153:13;;;6146:29;6191:13;;2806:102:43;;;;;;;;;;;;2796:113;;;;;;2777:132;;2956:272;;;;;;;;2993:10;2956:272;;;;3029:10;-1:-1:-1;;;;;2956:272:43;;;;;3064:9;2956:272;;;;3097:8;2956:272;;;;3147:25;;3129:15;:43;;;;:::i;:::-;2956:272;;3193:24;2956:272;;;;;;;2920:33;;;;;;;;;;;:308;;;;;;;;;;;;;;-1:-1:-1;;;;;;2920:308:43;-1:-1:-1;;;;;2920:308:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2920:308:43;;;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;3331:25:43;;3244:113;;;6417:25:49;;;6473:2;6458:18;;6451:34;;;6501:18;;;6494:34;;;;3244:113:43;3298:9;;-1:-1:-1;;;;;;3244:113:43;;;3268:16;;3244:113;;;;;6405:2:49;3244:113:43;;;3367:23;2039:1358;;;;;:::o;3999:567::-;4074:37;4114:33;;;;;;;;;;;4165:20;;;;;;:48;;;;;;;;:::i;:::-;;4157:94;;;;-1:-1:-1;;;4157:94:43;;6741:2:49;4157:94:43;;;6723:21:49;6780:2;6760:18;;;6753:30;6819:34;6799:18;;;6792:62;-1:-1:-1;;;6870:18:49;;;6863:31;6911:19;;4157:94:43;6539:397:49;4157:94:43;4288:14;:23;;;4269:15;:42;;4261:73;;;;-1:-1:-1;;;4261:73:43;;7143:2:49;4261:73:43;;;7125:21:49;7182:2;7162:18;;;7155:30;-1:-1:-1;;;7201:18:49;;;7194:48;7259:18;;4261:73:43;6941:342:49;4261:73:43;4345:20;;;:44;;-1:-1:-1;;4345:44:43;4368:21;4345:44;;;4405:9;;4345:44;4424:25;;;4451:21;;4405:68;;-1:-1:-1;;;4405:68:43;;-1:-1:-1;;;;;4424:25:43;;;4405:68;;;7462:51:49;7529:18;;;7522:34;;;;4405:9:43;;;:18;;7435::49;;4405:68:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4400:103;;4475:28;;-1:-1:-1;;;4475:28:43;;7769:2:49;4475:28:43;;;7751:21:49;7808:2;7788:18;;;7781:30;-1:-1:-1;;;7827:18:49;;;7820:48;7885:18;;4475:28:43;7567:342:49;4400:103:43;4519:40;;4542:16;;4519:40;;;;;4064:502;3999:567;:::o;3155:101:23:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;:::-;3155:101::o:0;3403:590:43:-;2334:13:23;:11;:13::i;:::-;3508:37:43::1;3548:33:::0;;;::::1;::::0;;;;;;;3600:20:::1;::::0;::::1;::::0;::::1;;:48;::::0;::::1;;;;;;:::i;:::-;;3592:91;;;::::0;-1:-1:-1;;;3592:91:43;;8116:2:49;3592:91:43::1;::::0;::::1;8098:21:49::0;8155:2;8135:18;;;8128:30;8194:32;8174:18;;;8167:60;8244:18;;3592:91:43::1;7914:354:49::0;3592:91:43::1;3742:23;::::0;::::1;::::0;3711:26:::1;::::0;;::::1;::::0;::::1;8402:19:49::0;;;8437:12;3711:26:43::1;;;;;;;;;;;;3701:37;;;;;;:64;3693:90;;;::::0;-1:-1:-1;;;3693:90:43;;8662:2:49;3693:90:43::1;::::0;::::1;8644:21:49::0;8701:2;8681:18;;;8674:30;-1:-1:-1;;;8720:18:49;;;8713:43;8773:18;;3693:90:43::1;8460:337:49::0;3693:90:43::1;3820:14;:23;;;3801:15;:42;;3793:70;;;::::0;-1:-1:-1;;;3793:70:43;;9004:2:49;3793:70:43::1;::::0;::::1;8986:21:49::0;9043:2;9023:18;;;9016:30;-1:-1:-1;;;9062:18:49;;;9055:45;9117:18;;3793:70:43::1;8802:339:49::0;3793:70:43::1;3874:20;::::0;::::1;:45:::0;;-1:-1:-1;;3874:45:43::1;3897:22;3874:45;::::0;;3935:51:::1;::::0;1000:25:49;;;3959:16:43;;3935:51:::1;::::0;988:2:49;973:18;3935:51:43::1;;;;;;;3498:495;3403:590:::0;;:::o;4572:323::-;4666:19;;-1:-1:-1;;;;;4666:19:43;4652:10;:33;4648:60;;4694:14;;-1:-1:-1;;;4694:14:43;;;;;;;;;;;4648:60;4736:6;4722:11;;:20;4718:58;;;4751:25;;-1:-1:-1;;;4751:25:43;;;;;;;;;;;4718:58;4801:6;4786:11;;:21;;;;;;;:::i;:::-;;;;-1:-1:-1;;4822:9:43;;:37;;-1:-1:-1;;;4822:37:43;;-1:-1:-1;;;;;7480:32:49;;;4822:37:43;;;7462:51:49;7529:18;;;7522:34;;;4822:9:43;;;;:18;;7435::49;;4822:37:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4817:71;;4868:20;;-1:-1:-1;;;4868:20:43;;;;;;;;;;;4817:71;4572:323;;:::o;1297:522::-;8870:21:24;4302:15;;-1:-1:-1;;;4302:15:24;;;;4301:16;;4348:14;;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;:16;;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:24;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:24;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:24;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:24;-1:-1:-1;;;5013:22:24;;;4979:67;-1:-1:-1;;;;;1483:24:43;::::1;::::0;;::::1;::::0;:47:::1;;-1:-1:-1::0;;;;;;1511:19:43;::::1;::::0;::::1;1483:47;1475:71;;;::::0;-1:-1:-1;;;1475:71:43;;4116:2:49;1475:71:43::1;::::0;::::1;4098:21:49::0;4155:2;4135:18;;;4128:30;-1:-1:-1;;;4174:18:49;;;4167:41;4225:18;;1475:71:43::1;3914:335:49::0;1475:71:43::1;1556:9;:40:::0;;-1:-1:-1;;;;;;1556:40:43::1;-1:-1:-1::0;;;;;1556:40:43;::::1;;::::0;;1606:21:::1;1621:5:::0;1606:14:::1;:21::i;:::-;1683:25;:45:::0;;;1779:11:::1;:33:::0;;;5066:101:24;;;;5100:23;;-1:-1:-1;;;;5100:23:24;;;5142:14;;-1:-1:-1;9432:50:49;;5142:14:24;;9420:2:49;9405:18;5142:14:24;;;;;;;5066:101;4092:1081;;;;;1297:522:43;;;;:::o;3405:215:23:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:23;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:23;;3562:1:::1;3534:31;::::0;::::1;1182:51:49::0;1155:18;;3534:31:23::1;1036:203:49::0;3485:91:23::1;3585:28;3604:8;3585:18;:28::i;:::-;3405:215:::0;:::o;2658:162::-;966:10:26;2717:7:23;1313:22;2570:8;-1:-1:-1;;;;;2570:8:23;;2441:144;2717:7;-1:-1:-1;;;;;2717:23:23;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:23;;966:10:26;2763:40:23;;;1182:51:49;1155:18;;2763:40:23;1036:203:49;3774:248:23;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:23;;-1:-1:-1;;;;;3941:19:23;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;1847:127::-;6931:20:24;:18;:20::i;:::-;1929:38:23::1;1954:12;1929:24;:38::i;7084:141:24:-:0;8870:21;8560:40;-1:-1:-1;;;8560:40:24;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:24;;;;;;;;;;;1980:235:23;6931:20:24;:18;:20::i;14:173:49:-;82:20;;-1:-1:-1;;;;;131:31:49;;121:42;;111:70;;177:1;174;167:12;111:70;14:173;;;:::o;192:186::-;251:6;304:2;292:9;283:7;279:23;275:32;272:52;;;320:1;317;310:12;272:52;343:29;362:9;343:29;:::i;:::-;333:39;192:186;-1:-1:-1;;;192:186:49:o;383:466::-;460:6;468;476;529:2;517:9;508:7;504:23;500:32;497:52;;;545:1;542;535:12;497:52;-1:-1:-1;;590:23:49;;;710:2;695:18;;682:32;;-1:-1:-1;813:2:49;798:18;;;785:32;;383:466;-1:-1:-1;383:466:49:o;1426:226::-;1485:6;1538:2;1526:9;1517:7;1513:23;1509:32;1506:52;;;1554:1;1551;1544:12;1506:52;-1:-1:-1;1599:23:49;;1426:226;-1:-1:-1;1426:226:49:o;1657:346::-;1725:6;1733;1786:2;1774:9;1765:7;1761:23;1757:32;1754:52;;;1802:1;1799;1792:12;1754:52;-1:-1:-1;;1847:23:49;;;1967:2;1952:18;;;1939:32;;-1:-1:-1;1657:346:49:o;2008:300::-;2076:6;2084;2137:2;2125:9;2116:7;2112:23;2108:32;2105:52;;;2153:1;2150;2143:12;2105:52;2176:29;2195:9;2176:29;:::i;:::-;2166:39;2274:2;2259:18;;;;2246:32;;-1:-1:-1;;;2008:300:49:o;2313:127::-;2374:10;2369:3;2365:20;2362:1;2355:31;2405:4;2402:1;2395:15;2429:4;2426:1;2419:15;2445:730;2748:25;;;-1:-1:-1;;;;;2809:32:49;;2804:2;2789:18;;2782:60;2873:2;2858:18;;2851:34;;;2916:2;2901:18;;2894:34;;;2959:3;2944:19;;2937:35;;;2735:3;2720:19;;3002:1;2991:13;;2981:144;;3047:10;3042:3;3038:20;3035:1;3028:31;3082:4;3079:1;3072:15;3110:4;3107:1;3100:15;2981:144;3162:6;3156:3;3145:9;3141:19;3134:35;2445:730;;;;;;;;;:::o;3414:495::-;3500:6;3508;3516;3524;3577:3;3565:9;3556:7;3552:23;3548:33;3545:53;;;3594:1;3591;3584:12;3545:53;3617:29;3636:9;3617:29;:::i;:::-;3607:39;;3665:38;3699:2;3688:9;3684:18;3665:38;:::i;:::-;3414:495;;3655:48;;-1:-1:-1;;;;3772:2:49;3757:18;;3744:32;;3873:2;3858:18;3845:32;;3414:495::o;4969:277::-;5036:6;5089:2;5077:9;5068:7;5064:23;5060:32;5057:52;;;5105:1;5102;5095:12;5057:52;5137:9;5131:16;5190:5;5183:13;5176:21;5169:5;5166:32;5156:60;;5212:1;5209;5202:12;5251:127;5312:10;5307:3;5303:20;5300:1;5293:31;5343:4;5340:1;5333:15;5367:4;5364:1;5357:15;5383:125;5448:9;;;5469:10;;;5466:36;;;5482:18;;:::i;:::-;5383:125;;;;:::o;5513:135::-;5552:3;5573:17;;;5570:43;;5593:18;;:::i;:::-;-1:-1:-1;5640:1:49;5629:13;;5513:135::o;9146:128::-;9213:9;;;9234:11;;;9231:37;;;9248:18;;:::i","linkReferences":{}},"methodIdentifiers":{"bridgeTransfers(bytes32)":"e0d9cbc4","completeBridgeTransfer(bytes32,bytes32)":"9f8f8799","counterpartyAddress()":"1f92c08e","initialize(address,address,uint256,uint256)":"eb990c59","initiateBridgeTransfer(uint256,bytes32,bytes32)":"1a8849a4","initiatorTimeLockDuration()":"2b3948bb","moveToken()":"e20c95ec","owner()":"8da5cb5b","poolBalance()":"96365d44","refundBridgeTransfer(bytes32)":"57808020","renounceOwnership()":"715018a6","setCounterpartyAddress(address)":"001a153e","transferOwnership(address)":"f2fde38b","withdrawMOVE(address,uint256)":"cacfb161"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"BridgeTransferHasBeenCompleted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BridgeTransferInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BridgeTransferStateNotInitialized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientMOVEBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSecret\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MOVETransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeLockNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimelockExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAmount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pre_image\",\"type\":\"bytes32\"}],\"name\":\"BridgeTransferCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_originator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"_hashLock\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_timeLock\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferInitiated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"BridgeTransferRefunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"bridgeTransfers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originator\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"hashLock\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"timeLock\",\"type\":\"uint256\"},{\"internalType\":\"enum AtomicBridgeInitiatorMOVE.MessageState\",\"name\":\"state\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"preImage\",\"type\":\"bytes32\"}],\"name\":\"completeBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counterpartyAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_moveToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_timeLockDuration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_initialPoolBalance\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"moveAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"hashLock\",\"type\":\"bytes32\"}],\"name\":\"initiateBridgeTransfer\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initiatorTimeLockDuration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"moveToken\",\"outputs\":[{\"internalType\":\"contract ERC20Upgradeable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"poolBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"refundBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_counterpartyAddress\",\"type\":\"address\"}],\"name\":\"setCounterpartyAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawMOVE\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}],\"OwnableInvalidOwner(address)\":[{\"details\":\"The owner is not a valid owner account. (eg. `address(0)`)\"}],\"OwnableUnauthorizedAccount(address)\":[{\"details\":\"The caller account is not authorized to perform an operation.\"}]},\"events\":{\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"}},\"kind\":\"dev\",\"methods\":{\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/AtomicBridgeInitiatorMOVE.sol\":\"AtomicBridgeInitiatorMOVE\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol\":{\"keccak256\":\"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6\",\"dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol\":{\"keccak256\":\"0xbb96dc9c468170c3224126e953de917e06332ec5909a3d85e6e5bb0df10c5139\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d14e6486e127e7e31c2ffccfc212c7ebaaecf8fb05677575128b449ee113def2\",\"dweb:/ipfs/QmabvyfStwBcum8mGfkmxcTV45rjyHmzHGCxfxyhmu48Yx\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol\":{\"keccak256\":\"0x880da465c203cec76b10d72dbd87c80f387df4102274f23eea1f9c9b0918792b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://399594cd8bb0143bc9e55e0f1d071d0d8c850a394fb7a319d50edd55d9ed822b\",\"dweb:/ipfs/QmbPZzgtT6LEm9CMqWfagQFwETbV1ztpECBB1DtQHrKiRz\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xe06a3f08a987af6ad2e1c1e774405d4fe08f1694b67517438b467cecf0da0ef7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df6f0c459663c9858b6cba2cda1d14a7d05a985bed6d2de72bd8e78c25ee79db\",\"dweb:/ipfs/QmeTTxZ7qVk9rjEv2R4CpCwdf8UMCcRqDNMvzNxHc3Fnn9\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"keccak256\":\"0x70f2f713b13b7ce4610bcd0ac9fec0f3cc43693b043abcb8dc40a42a726eb330\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c13d13304ac79a83ab1c30168967d19e2203342ebbd6a9bbce4db7550522dcbf\",\"dweb:/ipfs/QmeN5jKMN2vw5bhacr6tkg78afbTTZUeaacNHqjWt4Ew1r\"]},\"src/AtomicBridgeInitiatorMOVE.sol\":{\"keccak256\":\"0x5b19ecc44068863e3cb0727feb40cf40e2704ad78c1cbcc46943e3dec0c2d276\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0489c79d358a014df14338776376728f35be92bc9d6338eae228077db567dc7c\",\"dweb:/ipfs/QmXqVmMCHqZkSe41LSpoxoEiNRP7UJp5qU6bN3UykCQRzE\"]},\"src/IAtomicBridgeInitiatorMOVE.sol\":{\"keccak256\":\"0x3705cbaaf8910b786e5cb99d8d1bfa3f591add9a1d835703a69d6a77020ad841\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://12cca43905283adf49be8a23853952b90be09d3ba38b583fd8c2821e97ad8c0a\",\"dweb:/ipfs/QmaxHt6YUKb97dCz1rUjvjfM3XLcoLNvoFrUkDA6a77ER5\"]},\"src/MockMOVEToken.sol\":{\"keccak256\":\"0x71768bf12ff974ee8589bee8b951fa63c7db1f74f251c5997232643bc749a8e4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://38474add7fec1c814adf1ad614e4ca233b9f789f279690c698a3a94334c029cf\",\"dweb:/ipfs/Qmah2K8YEmLqnKjXcGvbSD55sm61zkKVvNMhehmrj6VzBs\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.26+commit.8a97fa7a"},"language":"Solidity","output":{"abi":[{"inputs":[],"type":"error","name":"BridgeTransferHasBeenCompleted"},{"inputs":[],"type":"error","name":"BridgeTransferInvalid"},{"inputs":[],"type":"error","name":"BridgeTransferStateNotInitialized"},{"inputs":[],"type":"error","name":"InsufficientMOVEBalance"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"InvalidSecret"},{"inputs":[],"type":"error","name":"MOVETransferFailed"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"type":"error","name":"OwnableInvalidOwner"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"type":"error","name":"OwnableUnauthorizedAccount"},{"inputs":[],"type":"error","name":"TimeLockNotExpired"},{"inputs":[],"type":"error","name":"TimelockExpired"},{"inputs":[],"type":"error","name":"Unauthorized"},{"inputs":[],"type":"error","name":"ZeroAddress"},{"inputs":[],"type":"error","name":"ZeroAmount"},{"inputs":[{"internalType":"bytes32","name":"_bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"pre_image","type":"bytes32","indexed":false}],"type":"event","name":"BridgeTransferCompleted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"_bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"address","name":"_originator","type":"address","indexed":true},{"internalType":"bytes32","name":"_recipient","type":"bytes32","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"bytes32","name":"_hashLock","type":"bytes32","indexed":false},{"internalType":"uint256","name":"_timeLock","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferInitiated","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"_bridgeTransferId","type":"bytes32","indexed":true}],"type":"event","name":"BridgeTransferRefunded","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"previousOwner","type":"address","indexed":true},{"internalType":"address","name":"newOwner","type":"address","indexed":true}],"type":"event","name":"OwnershipTransferred","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"bridgeTransfers","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"originator","type":"address"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"internalType":"uint256","name":"timeLock","type":"uint256"},{"internalType":"enum AtomicBridgeInitiatorMOVE.MessageState","name":"state","type":"uint8"}]},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"bytes32","name":"preImage","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"completeBridgeTransfer"},{"inputs":[],"stateMutability":"view","type":"function","name":"counterpartyAddress","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"_moveToken","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"_timeLockDuration","type":"uint256"},{"internalType":"uint256","name":"_initialPoolBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"initialize"},{"inputs":[{"internalType":"uint256","name":"moveAmount","type":"uint256"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"initiateBridgeTransfer","outputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"initiatorTimeLockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"moveToken","outputs":[{"internalType":"contract ERC20Upgradeable","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"poolBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"refundBridgeTransfer"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"renounceOwnership"},{"inputs":[{"internalType":"address","name":"_counterpartyAddress","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setCounterpartyAddress"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"transferOwnership"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"withdrawMOVE"}],"devdoc":{"kind":"dev","methods":{"owner()":{"details":"Returns the address of the current owner."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner."},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/AtomicBridgeInitiatorMOVE.sol":"AtomicBridgeInitiatorMOVE"},"evmVersion":"cancun","libraries":{}},"sources":{"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol":{"keccak256":"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a","urls":["bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6","dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol":{"keccak256":"0xbb96dc9c468170c3224126e953de917e06332ec5909a3d85e6e5bb0df10c5139","urls":["bzz-raw://d14e6486e127e7e31c2ffccfc212c7ebaaecf8fb05677575128b449ee113def2","dweb:/ipfs/QmabvyfStwBcum8mGfkmxcTV45rjyHmzHGCxfxyhmu48Yx"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol":{"keccak256":"0x880da465c203cec76b10d72dbd87c80f387df4102274f23eea1f9c9b0918792b","urls":["bzz-raw://399594cd8bb0143bc9e55e0f1d071d0d8c850a394fb7a319d50edd55d9ed822b","dweb:/ipfs/QmbPZzgtT6LEm9CMqWfagQFwETbV1ztpECBB1DtQHrKiRz"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0xe06a3f08a987af6ad2e1c1e774405d4fe08f1694b67517438b467cecf0da0ef7","urls":["bzz-raw://df6f0c459663c9858b6cba2cda1d14a7d05a985bed6d2de72bd8e78c25ee79db","dweb:/ipfs/QmeTTxZ7qVk9rjEv2R4CpCwdf8UMCcRqDNMvzNxHc3Fnn9"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol":{"keccak256":"0x70f2f713b13b7ce4610bcd0ac9fec0f3cc43693b043abcb8dc40a42a726eb330","urls":["bzz-raw://c13d13304ac79a83ab1c30168967d19e2203342ebbd6a9bbce4db7550522dcbf","dweb:/ipfs/QmeN5jKMN2vw5bhacr6tkg78afbTTZUeaacNHqjWt4Ew1r"],"license":"MIT"},"src/AtomicBridgeInitiatorMOVE.sol":{"keccak256":"0x5b19ecc44068863e3cb0727feb40cf40e2704ad78c1cbcc46943e3dec0c2d276","urls":["bzz-raw://0489c79d358a014df14338776376728f35be92bc9d6338eae228077db567dc7c","dweb:/ipfs/QmXqVmMCHqZkSe41LSpoxoEiNRP7UJp5qU6bN3UykCQRzE"],"license":"MIT"},"src/IAtomicBridgeInitiatorMOVE.sol":{"keccak256":"0x3705cbaaf8910b786e5cb99d8d1bfa3f591add9a1d835703a69d6a77020ad841","urls":["bzz-raw://12cca43905283adf49be8a23853952b90be09d3ba38b583fd8c2821e97ad8c0a","dweb:/ipfs/QmaxHt6YUKb97dCz1rUjvjfM3XLcoLNvoFrUkDA6a77ER5"],"license":"MIT"},"src/MockMOVEToken.sol":{"keccak256":"0x71768bf12ff974ee8589bee8b951fa63c7db1f74f251c5997232643bc749a8e4","urls":["bzz-raw://38474add7fec1c814adf1ad614e4ca233b9f789f279690c698a3a94334c029cf","dweb:/ipfs/Qmah2K8YEmLqnKjXcGvbSD55sm61zkKVvNMhehmrj6VzBs"],"license":"MIT"}},"version":1},"id":43} \ No newline at end of file diff --git a/protocol-units/bridge/service/abis/NativeBridge.json b/protocol-units/bridge/service/abis/NativeBridge.json index a1334d3f0..04d1a4d67 100644 --- a/protocol-units/bridge/service/abis/NativeBridge.json +++ b/protocol-units/bridge/service/abis/NativeBridge.json @@ -1 +1 @@ -{"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"RELAYER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"batchCompleteBridgeTransfer","inputs":[{"name":"bridgeTransferIds","type":"bytes32[]","internalType":"bytes32[]"},{"name":"initiators","type":"bytes32[]","internalType":"bytes32[]"},{"name":"recipients","type":"address[]","internalType":"address[]"},{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"nonces","type":"uint256[]","internalType":"uint256[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"completeBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"initiator","type":"bytes32","internalType":"bytes32"},{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_moveToken","type":"address","internalType":"address"},{"name":"_admin","type":"address","internalType":"address"},{"name":"_relayer","type":"address","internalType":"address"},{"name":"_maintainer","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"initiateBridgeTransfer","inputs":[{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"stateMutability":"nonpayable"},{"type":"function","name":"moveToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"type":"function","name":"noncesToIncomingBridgeTransferIds","inputs":[{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"incomingBridgeTransferId","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"outgoingBridgeTransfers","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"initiator","type":"address","internalType":"address"},{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"nonce","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"paused","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"togglePause","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"BridgeTransferCompleted","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"originator","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"recipient","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"nonce","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BridgeTransferInitiated","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"originator","type":"address","indexed":true,"internalType":"address"},{"name":"recipient","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"nonce","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"name":"account","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"name":"account","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"CompletedBridgeTransferId","inputs":[]},{"type":"error","name":"EnforcedPause","inputs":[]},{"type":"error","name":"ExpectedPause","inputs":[]},{"type":"error","name":"InvalidBridgeTransferId","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidLenghts","inputs":[]},{"type":"error","name":"MOVETransferFailed","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroAmount","inputs":[]}],"bytecode":{"object":"0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b611200806100d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610106575f3560e01c8063a217fddf1161009e578063d547741f1161006e578063d547741f14610230578063e20c95ec14610243578063e31219ee1461026e578063f26179e114610281578063f8c8765e146102e9575f5ffd5b8063a217fddf146101ef578063c4ae3168146101f6578063ca0c9c23146101fe578063cfc9ca8014610211575f5ffd5b806336568abe116100d957806336568abe1461017b5780635c975abb1461018e57806391d14854146101a5578063926d7d7f146101b8575f5ffd5b806301ffc9a71461010a5780630b417df414610132578063248a9ca3146101535780632f2ff15d14610166575b5f5ffd5b61011d610118366004610dde565b6102fc565b60405190151581526020015b60405180910390f35b610145610140366004610e0c565b610332565b604051908152602001610129565b610145610161366004610e2c565b6104f5565b610179610174366004610e5e565b610515565b005b610179610189366004610e5e565b610537565b5f5160206111ab5f395f51905f525460ff1661011d565b61011d6101b3366004610e5e565b61056f565b6101456040516b52454c415945525f524f4c4560a01b6020820152602c016040516020818303038152906040528051906020012081565b6101455f81565b6101796105a5565b61017961020c366004610fbc565b6105d8565b61014561021f366004610e2c565b60016020525f908152604090205481565b61017961023e366004610e5e565b6106ff565b600254610256906001600160a01b031681565b6040516001600160a01b039091168152602001610129565b61017961027c36600461109f565b61071b565b6102bf61028f366004610e2c565b5f6020819052908152604090208054600182015460028301546003909301546001600160a01b0390921692909184565b604080516001600160a01b0390951685526020850193909352918301526060820152608001610129565b6101796102f73660046110e2565b61076a565b5f6001600160e01b03198216637965db0b60e01b148061032c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f61033b610969565b5f821161035b57604051631f2a200560e01b815260040160405180910390fd5b6002546040516323b872dd60e01b8152336004820181905230602483015260448201859052916001600160a01b0316906323b872dd906064016020604051808303815f875af11580156103b0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103d49190611133565b6103f157604051631a67cf2760e31b815260040160405180910390fd5b80848460035f815461040290611152565b918290555060405160609490941b6bffffffffffffffffffffffff1916602085015260348401929092526054830152607482015260940160408051808303601f1901815282825280516020918201206080840183526001600160a01b038581168086528386018a81528686018a81526003805460608a019081525f87815280895289902099518a546001600160a01b03191696169590951789559151600189015551600288015591519582019590955554835188815292830152945086929185917fc2507e9f40fb7ddcc3fecc870160b2dcaf4fadc223535a7e23ca9fcf87c89e1b910160405180910390a45092915050565b5f9081525f51602061118b5f395f51905f52602052604090206001015490565b61051e826104f5565b6105278161099b565b61053183836109a5565b50505050565b6001600160a01b03811633146105605760405163334bd91960e11b815260040160405180910390fd5b61056a8282610a46565b505050565b5f9182525f51602061118b5f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f6105af8161099b565b5f5160206111ab5f395f51905f525460ff166105d0576105cd610abf565b50565b6105cd610b1e565b6040516b52454c415945525f524f4c4560a01b6020820152602c01604051602081830303815290604052805190602001206106128161099b565b85518551811480156106245750808551145b80156106305750808451145b801561063c5750808351145b61065957604051638b13b26360e01b815260040160405180910390fd5b5f5b818110156106f5576106ed88828151811061067857610678611176565b602002602001015188838151811061069257610692611176565b60200260200101518884815181106106ac576106ac611176565b60200260200101518885815181106106c6576106c6611176565b60200260200101518886815181106106e0576106e0611176565b6020026020010151610b66565b60010161065b565b5050505050505050565b610708826104f5565b6107118161099b565b6105318383610a46565b6040516b52454c415945525f524f4c4560a01b6020820152602c01604051602081830303815290604052805190602001206107558161099b565b6107628686868686610b66565b505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156107af5750825b90505f8267ffffffffffffffff1660011480156107cb5750303b155b9050811580156107d9575080155b156107f75760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561082157845460ff60401b1916600160401b1785555b6001600160a01b0389161580159061084157506001600160a01b03881615155b801561085557506001600160a01b03871615155b6108725760405163d92e233d60e01b815260040160405180910390fd5b61087a610cf5565b600280546001600160a01b0319166001600160a01b038b1617905561089f5f896109a5565b506040516b52454c415945525f524f4c4560a01b60208201526108db90602c0160405160208183030381529060405280519060200120886109a5565b506040516b52454c415945525f524f4c4560a01b602082015261091790602c0160405160208183030381529060405280519060200120876109a5565b50831561095e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b5f5160206111ab5f395f51905f525460ff16156109995760405163d93c066560e01b815260040160405180910390fd5b565b6105cd8133610d05565b5f5f51602061118b5f395f51905f526109be848461056f565b610a3d575f848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556109f33390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600191505061032c565b5f91505061032c565b5f5f51602061118b5f395f51905f52610a5f848461056f565b15610a3d575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4600191505061032c565b610ac7610d46565b5f5160206111ab5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b610b26610969565b5f5160206111ab5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833610b00565b60408051602081018690526bffffffffffffffffffffffff19606086901b16918101919091526054810183905260748101829052609401604051602081830303815290604052805190602001208514610bd257604051630118b74360e31b815260040160405180910390fd5b5f8181526001602052604090205415610bfe57604051633f968ce760e21b815260040160405180910390fd5b5f8181526001602052604090819020869055600254905163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb906044016020604051808303815f875af1158015610c5f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c839190611133565b610ca057604051631a67cf2760e31b815260040160405180910390fd5b826001600160a01b031684867fe8198bb3641f40f04ef209432a2832b3fa23e85c0360473bc30124d0e0cf60cf8585604051610ce6929190918252602082015260400190565b60405180910390a45050505050565b610cfd610d75565b610999610dbe565b610d0f828261056f565b610d425760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5160206111ab5f395f51905f525460ff1661099957604051638dfc202b60e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661099957604051631afcd79f60e31b815260040160405180910390fd5b610dc6610d75565b5f5160206111ab5f395f51905f52805460ff19169055565b5f60208284031215610dee575f5ffd5b81356001600160e01b031981168114610e05575f5ffd5b9392505050565b5f5f60408385031215610e1d575f5ffd5b50508035926020909101359150565b5f60208284031215610e3c575f5ffd5b5035919050565b80356001600160a01b0381168114610e59575f5ffd5b919050565b5f5f60408385031215610e6f575f5ffd5b82359150610e7f60208401610e43565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610ec557610ec5610e88565b604052919050565b5f67ffffffffffffffff821115610ee657610ee6610e88565b5060051b60200190565b5f82601f830112610eff575f5ffd5b8135610f12610f0d82610ecd565b610e9c565b8082825260208201915060208360051b860101925085831115610f33575f5ffd5b602085015b83811015610f50578035835260209283019201610f38565b5095945050505050565b5f82601f830112610f69575f5ffd5b8135610f77610f0d82610ecd565b8082825260208201915060208360051b860101925085831115610f98575f5ffd5b602085015b83811015610f5057610fae81610e43565b835260209283019201610f9d565b5f5f5f5f5f60a08688031215610fd0575f5ffd5b853567ffffffffffffffff811115610fe6575f5ffd5b610ff288828901610ef0565b955050602086013567ffffffffffffffff81111561100e575f5ffd5b61101a88828901610ef0565b945050604086013567ffffffffffffffff811115611036575f5ffd5b61104288828901610f5a565b935050606086013567ffffffffffffffff81111561105e575f5ffd5b61106a88828901610ef0565b925050608086013567ffffffffffffffff811115611086575f5ffd5b61109288828901610ef0565b9150509295509295909350565b5f5f5f5f5f60a086880312156110b3575f5ffd5b85359450602086013593506110ca60408701610e43565b94979396509394606081013594506080013592915050565b5f5f5f5f608085870312156110f5575f5ffd5b6110fe85610e43565b935061110c60208601610e43565b925061111a60408601610e43565b915061112860608601610e43565b905092959194509250565b5f60208284031215611143575f5ffd5b81518015158114610e05575f5ffd5b5f6001820161116f57634e487b7160e01b5f52601160045260245ffd5b5060010190565b634e487b7160e01b5f52603260045260245ffdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a26469706673582212203d2f25234616ce20402d5967e9d6b1ee0ba6f212c8ade4ec23b93d179802db8564736f6c634300081b0033","sourceMap":"476:4434:48:-:0;;;1126:53;;;;;;;;;-1:-1:-1;1150:22:48;:20;:22::i;:::-;476:4434;;7711:422:24;8870:21;7900:15;;;;;;;7896:76;;;7938:23;;-1:-1:-1;;;7938:23:24;;;;;;;;;;;7896:76;7985:14;;-1:-1:-1;;;;;7985:14:24;;;:34;7981:146;;8035:33;;-1:-1:-1;;;;;;8035:33:24;-1:-1:-1;;;;;8035:33:24;;;;;8087:29;;158:50:50;;;8087:29:24;;146:2:50;131:18;8087:29:24;;;;;;;7981:146;7760:373;7711:422::o;14:200:50:-;476:4434:48;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561000f575f5ffd5b5060043610610106575f3560e01c8063a217fddf1161009e578063d547741f1161006e578063d547741f14610230578063e20c95ec14610243578063e31219ee1461026e578063f26179e114610281578063f8c8765e146102e9575f5ffd5b8063a217fddf146101ef578063c4ae3168146101f6578063ca0c9c23146101fe578063cfc9ca8014610211575f5ffd5b806336568abe116100d957806336568abe1461017b5780635c975abb1461018e57806391d14854146101a5578063926d7d7f146101b8575f5ffd5b806301ffc9a71461010a5780630b417df414610132578063248a9ca3146101535780632f2ff15d14610166575b5f5ffd5b61011d610118366004610dde565b6102fc565b60405190151581526020015b60405180910390f35b610145610140366004610e0c565b610332565b604051908152602001610129565b610145610161366004610e2c565b6104f5565b610179610174366004610e5e565b610515565b005b610179610189366004610e5e565b610537565b5f5160206111ab5f395f51905f525460ff1661011d565b61011d6101b3366004610e5e565b61056f565b6101456040516b52454c415945525f524f4c4560a01b6020820152602c016040516020818303038152906040528051906020012081565b6101455f81565b6101796105a5565b61017961020c366004610fbc565b6105d8565b61014561021f366004610e2c565b60016020525f908152604090205481565b61017961023e366004610e5e565b6106ff565b600254610256906001600160a01b031681565b6040516001600160a01b039091168152602001610129565b61017961027c36600461109f565b61071b565b6102bf61028f366004610e2c565b5f6020819052908152604090208054600182015460028301546003909301546001600160a01b0390921692909184565b604080516001600160a01b0390951685526020850193909352918301526060820152608001610129565b6101796102f73660046110e2565b61076a565b5f6001600160e01b03198216637965db0b60e01b148061032c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f61033b610969565b5f821161035b57604051631f2a200560e01b815260040160405180910390fd5b6002546040516323b872dd60e01b8152336004820181905230602483015260448201859052916001600160a01b0316906323b872dd906064016020604051808303815f875af11580156103b0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103d49190611133565b6103f157604051631a67cf2760e31b815260040160405180910390fd5b80848460035f815461040290611152565b918290555060405160609490941b6bffffffffffffffffffffffff1916602085015260348401929092526054830152607482015260940160408051808303601f1901815282825280516020918201206080840183526001600160a01b038581168086528386018a81528686018a81526003805460608a019081525f87815280895289902099518a546001600160a01b03191696169590951789559151600189015551600288015591519582019590955554835188815292830152945086929185917fc2507e9f40fb7ddcc3fecc870160b2dcaf4fadc223535a7e23ca9fcf87c89e1b910160405180910390a45092915050565b5f9081525f51602061118b5f395f51905f52602052604090206001015490565b61051e826104f5565b6105278161099b565b61053183836109a5565b50505050565b6001600160a01b03811633146105605760405163334bd91960e11b815260040160405180910390fd5b61056a8282610a46565b505050565b5f9182525f51602061118b5f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f6105af8161099b565b5f5160206111ab5f395f51905f525460ff166105d0576105cd610abf565b50565b6105cd610b1e565b6040516b52454c415945525f524f4c4560a01b6020820152602c01604051602081830303815290604052805190602001206106128161099b565b85518551811480156106245750808551145b80156106305750808451145b801561063c5750808351145b61065957604051638b13b26360e01b815260040160405180910390fd5b5f5b818110156106f5576106ed88828151811061067857610678611176565b602002602001015188838151811061069257610692611176565b60200260200101518884815181106106ac576106ac611176565b60200260200101518885815181106106c6576106c6611176565b60200260200101518886815181106106e0576106e0611176565b6020026020010151610b66565b60010161065b565b5050505050505050565b610708826104f5565b6107118161099b565b6105318383610a46565b6040516b52454c415945525f524f4c4560a01b6020820152602c01604051602081830303815290604052805190602001206107558161099b565b6107628686868686610b66565b505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156107af5750825b90505f8267ffffffffffffffff1660011480156107cb5750303b155b9050811580156107d9575080155b156107f75760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561082157845460ff60401b1916600160401b1785555b6001600160a01b0389161580159061084157506001600160a01b03881615155b801561085557506001600160a01b03871615155b6108725760405163d92e233d60e01b815260040160405180910390fd5b61087a610cf5565b600280546001600160a01b0319166001600160a01b038b1617905561089f5f896109a5565b506040516b52454c415945525f524f4c4560a01b60208201526108db90602c0160405160208183030381529060405280519060200120886109a5565b506040516b52454c415945525f524f4c4560a01b602082015261091790602c0160405160208183030381529060405280519060200120876109a5565b50831561095e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b5f5160206111ab5f395f51905f525460ff16156109995760405163d93c066560e01b815260040160405180910390fd5b565b6105cd8133610d05565b5f5f51602061118b5f395f51905f526109be848461056f565b610a3d575f848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556109f33390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600191505061032c565b5f91505061032c565b5f5f51602061118b5f395f51905f52610a5f848461056f565b15610a3d575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4600191505061032c565b610ac7610d46565b5f5160206111ab5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b610b26610969565b5f5160206111ab5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833610b00565b60408051602081018690526bffffffffffffffffffffffff19606086901b16918101919091526054810183905260748101829052609401604051602081830303815290604052805190602001208514610bd257604051630118b74360e31b815260040160405180910390fd5b5f8181526001602052604090205415610bfe57604051633f968ce760e21b815260040160405180910390fd5b5f8181526001602052604090819020869055600254905163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb906044016020604051808303815f875af1158015610c5f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c839190611133565b610ca057604051631a67cf2760e31b815260040160405180910390fd5b826001600160a01b031684867fe8198bb3641f40f04ef209432a2832b3fa23e85c0360473bc30124d0e0cf60cf8585604051610ce6929190918252602082015260400190565b60405180910390a45050505050565b610cfd610d75565b610999610dbe565b610d0f828261056f565b610d425760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5160206111ab5f395f51905f525460ff1661099957604051638dfc202b60e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661099957604051631afcd79f60e31b815260040160405180910390fd5b610dc6610d75565b5f5160206111ab5f395f51905f52805460ff19169055565b5f60208284031215610dee575f5ffd5b81356001600160e01b031981168114610e05575f5ffd5b9392505050565b5f5f60408385031215610e1d575f5ffd5b50508035926020909101359150565b5f60208284031215610e3c575f5ffd5b5035919050565b80356001600160a01b0381168114610e59575f5ffd5b919050565b5f5f60408385031215610e6f575f5ffd5b82359150610e7f60208401610e43565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610ec557610ec5610e88565b604052919050565b5f67ffffffffffffffff821115610ee657610ee6610e88565b5060051b60200190565b5f82601f830112610eff575f5ffd5b8135610f12610f0d82610ecd565b610e9c565b8082825260208201915060208360051b860101925085831115610f33575f5ffd5b602085015b83811015610f50578035835260209283019201610f38565b5095945050505050565b5f82601f830112610f69575f5ffd5b8135610f77610f0d82610ecd565b8082825260208201915060208360051b860101925085831115610f98575f5ffd5b602085015b83811015610f5057610fae81610e43565b835260209283019201610f9d565b5f5f5f5f5f60a08688031215610fd0575f5ffd5b853567ffffffffffffffff811115610fe6575f5ffd5b610ff288828901610ef0565b955050602086013567ffffffffffffffff81111561100e575f5ffd5b61101a88828901610ef0565b945050604086013567ffffffffffffffff811115611036575f5ffd5b61104288828901610f5a565b935050606086013567ffffffffffffffff81111561105e575f5ffd5b61106a88828901610ef0565b925050608086013567ffffffffffffffff811115611086575f5ffd5b61109288828901610ef0565b9150509295509295909350565b5f5f5f5f5f60a086880312156110b3575f5ffd5b85359450602086013593506110ca60408701610e43565b94979396509394606081013594506080013592915050565b5f5f5f5f608085870312156110f5575f5ffd5b6110fe85610e43565b935061110c60208601610e43565b925061111a60408601610e43565b915061112860608601610e43565b905092959194509250565b5f60208284031215611143575f5ffd5b81518015158114610e05575f5ffd5b5f6001820161116f57634e487b7160e01b5f52601160045260245ffd5b5060010190565b634e487b7160e01b5f52603260045260245ffdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a26469706673582212203d2f25234616ce20402d5967e9d6b1ee0ba6f212c8ade4ec23b93d179802db8564736f6c634300081b0033","sourceMap":"476:4434:48:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3443:202:23;;;;;;:::i;:::-;;:::i;:::-;;;470:14:50;;463:22;445:41;;433:2;418:18;3443:202:23;;;;;;;;1693:963:48;;;;;;:::i;:::-;;:::i;:::-;;;994:25:50;;;982:2;967:18;1693:963:48;848:177:50;4759:191:23;;;;;;:::i;:::-;;:::i;5246:136::-;;;;;;:::i;:::-;;:::i;:::-;;6348:245;;;;;;:::i;:::-;;:::i;2692:145:27:-;-1:-1:-1;;;;;;;;;;;2821:9:27;;;2692:145;;3732:207:23;;;;;;:::i;:::-;;:::i;942:82:48:-;;991:32;;-1:-1:-1;;;991:32:48;;;8813:27:50;8856:12;;991:32:48;;;;;;;;;;;;981:43;;;;;;942:82;;2317:49:23;;2362:4;2317:49;;4798:110:48;;;:::i;2968:806::-;;;;;;:::i;:::-;;:::i;807:99::-;;;;;;:::i;:::-;;;;;;;;;;;;;;5662:138:23;;;;;;:::i;:::-;;:::i;913:23:48:-;;;;;-1:-1:-1;;;;;913:23:48;;;;;;-1:-1:-1;;;;;5486:32:50;;;5468:51;;5456:2;5441:18;913:23:48;5306:219:50;2662:300:48;;;;;;:::i;:::-;;:::i;711:90::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;711:90:48;;;;;;;;;;;;-1:-1:-1;;;;;6446:32:50;;;6428:51;;6510:2;6495:18;;6488:34;;;;6538:18;;;6531:34;6596:2;6581:18;;6574:34;6415:3;6400:19;711:90:48;6197:417:50;1217:470:48;;;;;;:::i;:::-;;:::i;3443:202:23:-;3528:4;-1:-1:-1;;;;;;3551:47:23;;-1:-1:-1;;;3551:47:23;;:87;;-1:-1:-1;;;;;;;;;;1134:40:28;;;3602:36:23;3544:94;3443:202;-1:-1:-1;;3443:202:23:o;1693:963:48:-;1816:24;2316:19:27;:17;:19::i;:::-;1915:1:48::1;1906:6;:10;1898:33;;;;-1:-1:-1::0;;;1898:33:48::1;;;;;;;;;;;;2090:9;::::0;:56:::1;::::0;-1:-1:-1;;;2090:56:48;;1998:10:::1;2090:56;::::0;::::1;7235:51:50::0;;;2132:4:48::1;7302:18:50::0;;;7295:60;7371:18;;;7364:34;;;1998:10:48;-1:-1:-1;;;;;2090:9:48::1;::::0;:22:::1;::::0;7208:18:50;;2090:56:48::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2085:90;;2155:20;;-1:-1:-1::0;;;2155:20:48::1;;;;;;;;;;;2085:90;2321:9;2332;2343:6;2353;;2351:8;;;;;:::i;:::-;::::0;;;;-1:-1:-1;2304:56:48::1;::::0;8161:2:50;8157:15;;;;-1:-1:-1;;8153:53:50;2304:56:48::1;::::0;::::1;8141:66:50::0;8223:12;;;8216:28;;;;8260:12;;;8253:28;8297:12;;;8290:28;8334:13;;2304:56:48::1;::::0;;;;::::1;-1:-1:-1::0;;2304:56:48;;;;;;2294:67;;2304:56:::1;2294:67:::0;;::::1;::::0;2461:60:::1;::::0;::::1;::::0;;-1:-1:-1;;;;;2461:60:48;;::::1;::::0;;;;;::::1;::::0;;;;;;;;;2514:6:::1;::::0;;2461:60;;;;;;-1:-1:-1;2417:41:48;;;;;;;;;:104;;;;-1:-1:-1;;;;;;2417:104:48::1;::::0;::::1;::::0;;;::::1;::::0;;;;-1:-1:-1;2417:104:48;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;;;2609:6;2537:79;;8532:25:50;;;8573:18;;;8566:34;2294:67:48;-1:-1:-1;2461:60:48;;;2294:67;;2537:79:::1;::::0;8505:18:50;2537:79:48::1;;;;;;;2626:23;1693:963:::0;;;;:::o;4759:191:23:-;4824:7;4919:14;;;-1:-1:-1;;;;;;;;;;;4919:14:23;;;;;:24;;;;4759:191::o;5246:136::-;5320:18;5333:4;5320:12;:18::i;:::-;3191:16;3202:4;3191:10;:16::i;:::-;5350:25:::1;5361:4;5367:7;5350:10;:25::i;:::-;;5246:136:::0;;;:::o;6348:245::-;-1:-1:-1;;;;;6441:34:23;;966:10:26;6441:34:23;6437:102;;6498:30;;-1:-1:-1;;;6498:30:23;;;;;;;;;;;6437:102;6549:37;6561:4;6567:18;6549:11;:37::i;:::-;;6348:245;;:::o;3732:207::-;3809:4;3901:14;;;-1:-1:-1;;;;;;;;;;;3901:14:23;;;;;;;;-1:-1:-1;;;;;3901:31:23;;;;;;;;;;;;;;;3732:207::o;4798:110:48:-;2362:4:23;3191:16;2362:4;3191:10;:16::i;:::-;-1:-1:-1;;;;;;;;;;;2821:9:27;;;4869:32:48::1;;4891:10;:8;:10::i;:::-;4798:110:::0;:::o;4869:32::-:1;4880:8;:6;:8::i;2968:806::-:0;991:32;;-1:-1:-1;;;991:32:48;;;8813:27:50;8856:12;;991:32:48;;;;;;;;;;;;981:43;;;;;;3191:16:23;3202:4;3191:10;:16::i;:::-;3255:24:48;;3365:17;;:27;::::1;:58:::0;::::1;;;;3417:6;3396:10;:17;:27;3365:58;:86;;;;;3445:6;3427:7;:14;:24;3365:86;:129;;;;;3488:6;3471;:13;:23;3365:129;3344:190;;;;-1:-1:-1::0;;;3344:190:48::1;;;;;;;;;;;;3617:9;3612:156;3632:6;3628:1;:10;3612:156;;;3659:98;3683:17;3701:1;3683:20;;;;;;;;:::i;:::-;;;;;;;3705:10;3716:1;3705:13;;;;;;;;:::i;:::-;;;;;;;3720:10;3731:1;3720:13;;;;;;;;:::i;:::-;;;;;;;3735:7;3743:1;3735:10;;;;;;;;:::i;:::-;;;;;;;3747:6;3754:1;3747:9;;;;;;;;:::i;:::-;;;;;;;3659:23;:98::i;:::-;3640:3;;3612:156;;;;3228:546;2968:806:::0;;;;;;:::o;5662:138:23:-;5737:18;5750:4;5737:12;:18::i;:::-;3191:16;3202:4;3191:10;:16::i;:::-;5767:26:::1;5779:4;5785:7;5767:11;:26::i;2662:300:48:-:0;991:32;;-1:-1:-1;;;991:32:48;;;8813:27:50;8856:12;;991:32:48;;;;;;;;;;;;981:43;;;;;;3191:16:23;3202:4;3191:10;:16::i;:::-;2877:78:48::1;2901:16;2919:9;2930;2941:6;2949:5;2877:23;:78::i;:::-;2662:300:::0;;;;;;:::o;1217:470::-;8870:21:24;4302:15;;-1:-1:-1;;;4302:15:24;;;;4301:16;;4348:14;;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;:16;;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:24;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:24;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:24;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:24;-1:-1:-1;;;5013:22:24;;;4979:67;-1:-1:-1;;;;;1349:24:48;::::1;::::0;;::::1;::::0;:48:::1;;-1:-1:-1::0;;;;;;1377:20:48;::::1;::::0;::::1;1349:48;:74;;;;-1:-1:-1::0;;;;;;1401:22:48;::::1;::::0;::::1;1349:74;1341:98;;;;-1:-1:-1::0;;;1341:98:48::1;;;;;;;;;;;;1449:17;:15;:17::i;:::-;1476:9;:30:::0;;-1:-1:-1;;;;;;1476:30:48::1;-1:-1:-1::0;;;;;1476:30:48;::::1;;::::0;;1516:38:::1;-1:-1:-1::0;1547:6:48;1516:10:::1;:38::i;:::-;-1:-1:-1::0;991:32:48::1;::::0;-1:-1:-1;;;991:32:48::1;::::0;::::1;8813:27:50::0;1564:34:48::1;::::0;8856:12:50;;991:32:48::1;;;;;;;;;;;;981:43;;;;;;1589:8;1564:10;:34::i;:::-;-1:-1:-1::0;991:32:48::1;::::0;-1:-1:-1;;;991:32:48::1;::::0;::::1;8813:27:50::0;1643:37:48::1;::::0;8856:12:50;;991:32:48::1;;;;;;;;;;;;981:43;;;;;;1668:11;1643:10;:37::i;:::-;;5070:14:24::0;5066:101;;;5100:23;;-1:-1:-1;;;;5100:23:24;;;5142:14;;-1:-1:-1;9164:50:50;;5142:14:24;;9152:2:50;9137:18;5142:14:24;;;;;;;5066:101;4092:1081;;;;;1217:470:48;;;;:::o;2905:128:27:-;-1:-1:-1;;;;;;;;;;;2821:9:27;;;2966:61;;;3001:15;;-1:-1:-1;;;3001:15:27;;;;;;;;;;;2966:61;2905:128::o;4148:103:23:-;4214:30;4225:4;966:10:26;4214::23;:30::i;7270:387::-;7347:4;-1:-1:-1;;;;;;;;;;;7437:22:23;7445:4;7451:7;7437;:22::i;:::-;7432:219;;7475:8;:14;;;;;;;;;;;-1:-1:-1;;;;;7475:31:23;;;;;;;;;:38;;-1:-1:-1;;7475:38:23;7509:4;7475:38;;;7559:12;966:10:26;;887:96;7559:12:23;-1:-1:-1;;;;;7532:40:23;7550:7;-1:-1:-1;;;;;7532:40:23;7544:4;7532:40;;;;;;;;;;7593:4;7586:11;;;;;7432:219;7635:5;7628:12;;;;;7892:388;7970:4;-1:-1:-1;;;;;;;;;;;8059:22:23;8067:4;8073:7;8059;:22::i;:::-;8055:219;;;8131:5;8097:14;;;;;;;;;;;-1:-1:-1;;;;;8097:31:23;;;;;;;;;;:39;;-1:-1:-1;;8097:39:23;;;8155:40;966:10:26;;8097:14:23;;8155:40;;8131:5;8155:40;8216:4;8209:11;;;;;3674:178:27;2563:16;:14;:16::i;:::-;-1:-1:-1;;;;;;;;;;;3791:17:27;;-1:-1:-1;;3791:17:27::1;::::0;;3823:22:::1;966:10:26::0;3832:12:27::1;3823:22;::::0;-1:-1:-1;;;;;5486:32:50;;;5468:51;;5456:2;5441:18;3823:22:27::1;;;;;;;3722:130;3674:178::o:0;3366:176::-;2316:19;:17;:19::i;:::-;-1:-1:-1;;;;;;;;;;;3484:16:27;;-1:-1:-1;;3484:16:27::1;3496:4;3484:16;::::0;;3515:20:::1;966:10:26::0;3522:12:27::1;887:96:26::0;3780:1012:48;4161:53;;;;;;9646:19:50;;;-1:-1:-1;;9703:2:50;9699:15;;;9695:53;9681:12;;;9674:75;;;;9765:12;;;9758:28;;;9802:12;;;9795:28;;;9839:13;;4161:53:48;;;;;;;;;;;;4151:64;;;;;;4131:16;:84;4110:154;;;;-1:-1:-1;;;4110:154:48;;;;;;;;;;;;4403:3;4351:40;;;:33;:40;;;;;;:56;4343:94;;;;-1:-1:-1;;;4343:94:48;;;;;;;;;;;;4497:40;;;;:33;:40;;;;;;;:59;;;4625:9;;:37;;-1:-1:-1;;;4625:37:48;;-1:-1:-1;;;;;10055:32:50;;;4625:37:48;;;10037:51:50;10104:18;;;10097:34;;;4625:9:48;;;;:18;;10010::50;;4625:37:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4620:71;;4671:20;;-1:-1:-1;;;4671:20:48;;;;;;;;;;;4620:71;4760:9;-1:-1:-1;;;;;4707:78:48;4749:9;4731:16;4707:78;4771:6;4779:5;4707:78;;;;;;8532:25:50;;;8588:2;8573:18;;8566:34;8520:2;8505:18;;8358:248;4707:78:48;;;;;;;;3780:1012;;;;;:::o;1836:97:27:-;6931:20:24;:18;:20::i;:::-;1899:27:27::1;:25;:27::i;4381:197:23:-:0;4469:22;4477:4;4483:7;4469;:22::i;:::-;4464:108;;4514:47;;-1:-1:-1;;;4514:47:23;;-1:-1:-1;;;;;10055:32:50;;4514:47:23;;;10037:51:50;10104:18;;;10097:34;;;10010:18;;4514:47:23;;;;;;;4464:108;4381:197;;:::o;3105:126:27:-;-1:-1:-1;;;;;;;;;;;2821:9:27;;;3163:62;;3199:15;;-1:-1:-1;;;3199:15:27;;;;;;;;;;;7084:141:24;8870:21;8560:40;-1:-1:-1;;;8560:40:24;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:24;;;;;;;;;;;1939:156:27;6931:20:24;:18;:20::i;:::-;-1:-1:-1;;;;;;;;;;;2071:17:27;;-1:-1:-1;;2071:17:27::1;::::0;;1939:156::o;14:286:50:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:50;;209:43;;199:71;;266:1;263;256:12;199:71;289:5;14:286;-1:-1:-1;;;14:286:50:o;497:346::-;565:6;573;626:2;614:9;605:7;601:23;597:32;594:52;;;642:1;639;632:12;594:52;-1:-1:-1;;687:23:50;;;807:2;792:18;;;779:32;;-1:-1:-1;497:346:50:o;1030:226::-;1089:6;1142:2;1130:9;1121:7;1117:23;1113:32;1110:52;;;1158:1;1155;1148:12;1110:52;-1:-1:-1;1203:23:50;;1030:226;-1:-1:-1;1030:226:50:o;1261:173::-;1329:20;;-1:-1:-1;;;;;1378:31:50;;1368:42;;1358:70;;1424:1;1421;1414:12;1358:70;1261:173;;;:::o;1439:300::-;1507:6;1515;1568:2;1556:9;1547:7;1543:23;1539:32;1536:52;;;1584:1;1581;1574:12;1536:52;1629:23;;;-1:-1:-1;1695:38:50;1729:2;1714:18;;1695:38;:::i;:::-;1685:48;;1439:300;;;;;:::o;1744:127::-;1805:10;1800:3;1796:20;1793:1;1786:31;1836:4;1833:1;1826:15;1860:4;1857:1;1850:15;1876:275;1947:2;1941:9;2012:2;1993:13;;-1:-1:-1;;1989:27:50;1977:40;;2047:18;2032:34;;2068:22;;;2029:62;2026:88;;;2094:18;;:::i;:::-;2130:2;2123:22;1876:275;;-1:-1:-1;1876:275:50:o;2156:183::-;2216:4;2249:18;2241:6;2238:30;2235:56;;;2271:18;;:::i;:::-;-1:-1:-1;2316:1:50;2312:14;2328:4;2308:25;;2156:183::o;2344:723::-;2398:5;2451:3;2444:4;2436:6;2432:17;2428:27;2418:55;;2469:1;2466;2459:12;2418:55;2509:6;2496:20;2536:64;2552:47;2592:6;2552:47;:::i;:::-;2536:64;:::i;:::-;2624:3;2648:6;2643:3;2636:19;2680:4;2675:3;2671:14;2664:21;;2741:4;2731:6;2728:1;2724:14;2716:6;2712:27;2708:38;2694:52;;2769:3;2761:6;2758:15;2755:35;;;2786:1;2783;2776:12;2755:35;2822:4;2814:6;2810:17;2836:200;2852:6;2847:3;2844:15;2836:200;;;2944:17;;2974:18;;3021:4;3012:14;;;;2869;2836:200;;;-1:-1:-1;3054:7:50;2344:723;-1:-1:-1;;;;;2344:723:50:o;3072:675::-;3126:5;3179:3;3172:4;3164:6;3160:17;3156:27;3146:55;;3197:1;3194;3187:12;3146:55;3237:6;3224:20;3264:64;3280:47;3320:6;3280:47;:::i;3264:64::-;3352:3;3376:6;3371:3;3364:19;3408:4;3403:3;3399:14;3392:21;;3469:4;3459:6;3456:1;3452:14;3444:6;3440:27;3436:38;3422:52;;3497:3;3489:6;3486:15;3483:35;;;3514:1;3511;3504:12;3483:35;3550:4;3542:6;3538:17;3564:152;3580:6;3575:3;3572:15;3564:152;;;3648:23;3667:3;3648:23;:::i;:::-;3636:36;;3701:4;3692:14;;;;3597;3564:152;;3752:1318;3972:6;3980;3988;3996;4004;4057:3;4045:9;4036:7;4032:23;4028:33;4025:53;;;4074:1;4071;4064:12;4025:53;4114:9;4101:23;4147:18;4139:6;4136:30;4133:50;;;4179:1;4176;4169:12;4133:50;4202:61;4255:7;4246:6;4235:9;4231:22;4202:61;:::i;:::-;4192:71;;;4316:2;4305:9;4301:18;4288:32;4345:18;4335:8;4332:32;4329:52;;;4377:1;4374;4367:12;4329:52;4400:63;4455:7;4444:8;4433:9;4429:24;4400:63;:::i;:::-;4390:73;;;4516:2;4505:9;4501:18;4488:32;4545:18;4535:8;4532:32;4529:52;;;4577:1;4574;4567:12;4529:52;4600:63;4655:7;4644:8;4633:9;4629:24;4600:63;:::i;:::-;4590:73;;;4716:2;4705:9;4701:18;4688:32;4745:18;4735:8;4732:32;4729:52;;;4777:1;4774;4767:12;4729:52;4800:63;4855:7;4844:8;4833:9;4829:24;4800:63;:::i;:::-;4790:73;;;4916:3;4905:9;4901:19;4888:33;4946:18;4936:8;4933:32;4930:52;;;4978:1;4975;4968:12;4930:52;5001:63;5056:7;5045:8;5034:9;5030:24;5001:63;:::i;:::-;4991:73;;;3752:1318;;;;;;;;:::o;5530:662::-;5625:6;5633;5641;5649;5657;5710:3;5698:9;5689:7;5685:23;5681:33;5678:53;;;5727:1;5724;5717:12;5678:53;5772:23;;;-1:-1:-1;5892:2:50;5877:18;;5864:32;;-1:-1:-1;5941:38:50;5975:2;5960:18;;5941:38;:::i;:::-;5530:662;;;;-1:-1:-1;5931:48:50;;6052:2;6037:18;;6024:32;;-1:-1:-1;6155:3:50;6140:19;6127:33;;5530:662;-1:-1:-1;;5530:662:50:o;6619:409::-;6705:6;6713;6721;6729;6782:3;6770:9;6761:7;6757:23;6753:33;6750:53;;;6799:1;6796;6789:12;6750:53;6822:29;6841:9;6822:29;:::i;:::-;6812:39;;6870:38;6904:2;6893:9;6889:18;6870:38;:::i;:::-;6860:48;;6927:38;6961:2;6950:9;6946:18;6927:38;:::i;:::-;6917:48;;6984:38;7018:2;7007:9;7003:18;6984:38;:::i;:::-;6974:48;;6619:409;;;;;;;:::o;7409:277::-;7476:6;7529:2;7517:9;7508:7;7504:23;7500:32;7497:52;;;7545:1;7542;7535:12;7497:52;7577:9;7571:16;7630:5;7623:13;7616:21;7609:5;7606:32;7596:60;;7652:1;7649;7642:12;7691:232;7730:3;7751:17;;;7748:140;;7810:10;7805:3;7801:20;7798:1;7791:31;7845:4;7842:1;7835:15;7873:4;7870:1;7863:15;7748:140;-1:-1:-1;7915:1:50;7904:13;;7691:232::o;8879:127::-;8940:10;8935:3;8931:20;8928:1;8921:31;8971:4;8968:1;8961:15;8995:4;8992:1;8985:15","linkReferences":{}},"methodIdentifiers":{"DEFAULT_ADMIN_ROLE()":"a217fddf","RELAYER_ROLE()":"926d7d7f","batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])":"ca0c9c23","completeBridgeTransfer(bytes32,bytes32,address,uint256,uint256)":"e31219ee","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","initialize(address,address,address,address)":"f8c8765e","initiateBridgeTransfer(bytes32,uint256)":"0b417df4","moveToken()":"e20c95ec","noncesToIncomingBridgeTransferIds(uint256)":"cfc9ca80","outgoingBridgeTransfers(bytes32)":"f26179e1","paused()":"5c975abb","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","togglePause()":"c4ae3168"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CompletedBridgeTransferId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBridgeTransferId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidLenghts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MOVETransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAmount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originator\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"originator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferInitiated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RELAYER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"bridgeTransferIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"initiators\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"nonces\",\"type\":\"uint256[]\"}],\"name\":\"batchCompleteBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"initiator\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"completeBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_moveToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_relayer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_maintainer\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initiateBridgeTransfer\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"moveToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"noncesToIncomingBridgeTransferIds\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"incomingBridgeTransferId\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"outgoingBridgeTransfers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"togglePause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"EnforcedPause()\":[{\"details\":\"The operation failed because the contract is paused.\"}],\"ExpectedPause()\":[{\"details\":\"The operation failed because the contract is not paused.\"}],\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}]},\"events\":{\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"},\"Paused(address)\":{\"details\":\"Emitted when the pause is triggered by `account`.\"},\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted signaling this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"},\"Unpaused(address)\":{\"details\":\"Emitted when the pause is lifted by `account`.\"}},\"kind\":\"dev\",\"methods\":{\"batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])\":{\"details\":\"Completes multiple bridge transfers\",\"params\":{\"amounts\":\"The amounts to transfer\",\"bridgeTransferIds\":\"Unique identifiers for the BridgeTransfers\",\"initiators\":\"The addresses on the other chain that originated the transfer of funds\",\"nonces\":\"The seed nonces to generate the bridgeTransferIds\",\"recipients\":\"The addresses on this chain to which to transfer funds\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"initiateBridgeTransfer(bytes32,uint256)\":{\"details\":\"Creates a new bridge\",\"params\":{\"amount\":\"The amount of MOVE to send\",\"recipient\":\"The address on the other chain to which to transfer funds\"},\"returns\":{\"bridgeTransferId\":\"A unique id representing this BridgeTransfer\"}},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"See {IERC165-supportsInterface}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/NativeBridge.sol\":\"NativeBridge\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol\":{\"keccak256\":\"0x6662ec4e5cefca03eeadd073e9469df8d2944bb2ee8ec8f7622c2c46aab5f225\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4d8544c6f8daa4d1bc215c6a72fe0acdb748664a105b0e5efc19295667521d45\",\"dweb:/ipfs/QmdGWqdnXT8S3RgCR6aV8XHZrsybieMQLLnug1NtpSjEXN\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol\":{\"keccak256\":\"0x92915b7f7f642c6be3f65bfd1522feb5d5b6ef25f755f4dbb51df32c868f2f97\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://85ad36d5cc7e190e1ee6c94b24659bc3a31396c4c36b6ffa6a509e10661f8007\",\"dweb:/ipfs/QmPFyc4zMh2zo6YWZt25gjm3YdR2hg6wGETaWw256fMmJJ\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol\":{\"keccak256\":\"0xddfe0afa85367153020524d383fe0bc9a1545f343019ddf33f98f392887047f0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://62f3df350ce83190e81673e424dd47d9ee8ffb121a6f72994c75c8ae3fc9dded\",\"dweb:/ipfs/QmaA2CMuqESmdgVao4XzCL4aRwcZW1xjUXpY7RqHZfQoAV\"]},\"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60\",\"dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009\",\"dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e\",\"dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX\"]},\"src/INativeBridge.sol\":{\"keccak256\":\"0x2e96e622084c1832fc426dc1a08b32b19f01bb5da991b45f85ec462e0f4371f2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2064254d915d06271b574e5c9dba8d3c0b95dda627d6aaa058b3cfe9cc65a25e\",\"dweb:/ipfs/QmUhUMshXy1yeE6WmeNT34q9tL4TmLyzYuDMBqMj6u5E7W\"]},\"src/NativeBridge.sol\":{\"keccak256\":\"0x26f031841619771361ffff1998f2cd01177ae38294ea29194c078c5cd8397fc5\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://0db13a61b6273df59546df1ac322d245da26f54700f77060e20c9c9786b6712b\",\"dweb:/ipfs/QmfGQkGWxyaGcrYnxhe9zukw4uBuZqsrGWMKw7kjqnJHtS\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.27+commit.40a35a09"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[],"type":"error","name":"CompletedBridgeTransferId"},{"inputs":[],"type":"error","name":"EnforcedPause"},{"inputs":[],"type":"error","name":"ExpectedPause"},{"inputs":[],"type":"error","name":"InvalidBridgeTransferId"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"InvalidLenghts"},{"inputs":[],"type":"error","name":"MOVETransferFailed"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[],"type":"error","name":"ZeroAddress"},{"inputs":[],"type":"error","name":"ZeroAmount"},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"originator","type":"bytes32","indexed":true},{"internalType":"address","name":"recipient","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint256","name":"nonce","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferCompleted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"address","name":"originator","type":"address","indexed":true},{"internalType":"bytes32","name":"recipient","type":"bytes32","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint256","name":"nonce","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferInitiated","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"account","type":"address","indexed":false}],"type":"event","name":"Paused","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[{"internalType":"address","name":"account","type":"address","indexed":false}],"type":"event","name":"Unpaused","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"RELAYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32[]","name":"bridgeTransferIds","type":"bytes32[]"},{"internalType":"bytes32[]","name":"initiators","type":"bytes32[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"nonces","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function","name":"batchCompleteBridgeTransfer"},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"bytes32","name":"initiator","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"completeBridgeTransfer"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"_moveToken","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_relayer","type":"address"},{"internalType":"address","name":"_maintainer","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"initialize"},{"inputs":[{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"initiateBridgeTransfer","outputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"moveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function","name":"noncesToIncomingBridgeTransferIds","outputs":[{"internalType":"bytes32","name":"incomingBridgeTransferId","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}],"stateMutability":"view","type":"function","name":"outgoingBridgeTransfers","outputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"togglePause"}],"devdoc":{"kind":"dev","methods":{"batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])":{"details":"Completes multiple bridge transfers","params":{"amounts":"The amounts to transfer","bridgeTransferIds":"Unique identifiers for the BridgeTransfers","initiators":"The addresses on the other chain that originated the transfer of funds","nonces":"The seed nonces to generate the bridgeTransferIds","recipients":"The addresses on this chain to which to transfer funds"}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"initiateBridgeTransfer(bytes32,uint256)":{"details":"Creates a new bridge","params":{"amount":"The amount of MOVE to send","recipient":"The address on the other chain to which to transfer funds"},"returns":{"bridgeTransferId":"A unique id representing this BridgeTransfer"}},"paused()":{"details":"Returns true if the contract is paused, and false otherwise."},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"See {IERC165-supportsInterface}."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/NativeBridge.sol":"NativeBridge"},"evmVersion":"cancun","libraries":{}},"sources":{"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol":{"keccak256":"0x6662ec4e5cefca03eeadd073e9469df8d2944bb2ee8ec8f7622c2c46aab5f225","urls":["bzz-raw://4d8544c6f8daa4d1bc215c6a72fe0acdb748664a105b0e5efc19295667521d45","dweb:/ipfs/QmdGWqdnXT8S3RgCR6aV8XHZrsybieMQLLnug1NtpSjEXN"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol":{"keccak256":"0x92915b7f7f642c6be3f65bfd1522feb5d5b6ef25f755f4dbb51df32c868f2f97","urls":["bzz-raw://85ad36d5cc7e190e1ee6c94b24659bc3a31396c4c36b6ffa6a509e10661f8007","dweb:/ipfs/QmPFyc4zMh2zo6YWZt25gjm3YdR2hg6wGETaWw256fMmJJ"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol":{"keccak256":"0xddfe0afa85367153020524d383fe0bc9a1545f343019ddf33f98f392887047f0","urls":["bzz-raw://62f3df350ce83190e81673e424dd47d9ee8ffb121a6f72994c75c8ae3fc9dded","dweb:/ipfs/QmaA2CMuqESmdgVao4XzCL4aRwcZW1xjUXpY7RqHZfQoAV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol":{"keccak256":"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d","urls":["bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60","dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4","urls":["bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009","dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"keccak256":"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c","urls":["bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e","dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX"],"license":"MIT"},"src/INativeBridge.sol":{"keccak256":"0x2e96e622084c1832fc426dc1a08b32b19f01bb5da991b45f85ec462e0f4371f2","urls":["bzz-raw://2064254d915d06271b574e5c9dba8d3c0b95dda627d6aaa058b3cfe9cc65a25e","dweb:/ipfs/QmUhUMshXy1yeE6WmeNT34q9tL4TmLyzYuDMBqMj6u5E7W"],"license":"MIT"},"src/NativeBridge.sol":{"keccak256":"0x26f031841619771361ffff1998f2cd01177ae38294ea29194c078c5cd8397fc5","urls":["bzz-raw://0db13a61b6273df59546df1ac322d245da26f54700f77060e20c9c9786b6712b","dweb:/ipfs/QmfGQkGWxyaGcrYnxhe9zukw4uBuZqsrGWMKw7kjqnJHtS"],"license":"UNLICENSED"}},"version":1},"id":48} \ No newline at end of file +{"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"RELAYER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"batchCompleteBridgeTransfer","inputs":[{"name":"bridgeTransferIds","type":"bytes32[]","internalType":"bytes32[]"},{"name":"initiators","type":"bytes32[]","internalType":"bytes32[]"},{"name":"recipients","type":"address[]","internalType":"address[]"},{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"nonces","type":"uint256[]","internalType":"uint256[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"completeBridgeTransfer","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"initiator","type":"bytes32","internalType":"bytes32"},{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"idsToIncomingNonces","inputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"nonce","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_moveToken","type":"address","internalType":"address"},{"name":"_admin","type":"address","internalType":"address"},{"name":"_relayer","type":"address","internalType":"address"},{"name":"_maintainer","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"initiateBridgeTransfer","inputs":[{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"}],"stateMutability":"nonpayable"},{"type":"function","name":"moveToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"type":"function","name":"noncesToOutgoingTransfers","inputs":[{"name":"nonce","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"bridgeTransferId","type":"bytes32","internalType":"bytes32"},{"name":"initiator","type":"address","internalType":"address"},{"name":"recipient","type":"bytes32","internalType":"bytes32"},{"name":"amount","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"paused","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"togglePause","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"BridgeTransferCompleted","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"originator","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"recipient","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"nonce","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BridgeTransferInitiated","inputs":[{"name":"bridgeTransferId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"originator","type":"address","indexed":true,"internalType":"address"},{"name":"recipient","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"nonce","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"name":"account","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"name":"account","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"CompletedBridgeTransferId","inputs":[]},{"type":"error","name":"EnforcedPause","inputs":[]},{"type":"error","name":"ExpectedPause","inputs":[]},{"type":"error","name":"InvalidBridgeTransferId","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidLenghts","inputs":[]},{"type":"error","name":"InvalidNonce","inputs":[]},{"type":"error","name":"MOVETransferFailed","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroAmount","inputs":[]}],"bytecode":{"object":"0x6080806040523460d0577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c1660c1576002600160401b03196001600160401b03821601605c575b604051610fd390816100d58239f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80604d565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146109dc575080630b417df4146107f35780631058648f146107a0578063248a9ca3146107825780632f2ff15d1461075657806336568abe146107125780635c975abb146106e457806391d148541461068f578063926d7d7f1461066d578063a217fddf14610653578063c4ae316814610577578063c509918f1461054d578063ca0c9c2314610388578063d547741f14610357578063e20c95ec1461032f578063e31219ee146102ee5763f8c8765e146100d7575f80fd5b346102ea5760803660031901126102ea576004356001600160a01b038116908190036102ea57610105610a2f565b9061010e610a45565b91606435906001600160a01b03821682036102ea575f80516020610f7e833981519152549360ff8560401c16159467ffffffffffffffff8116801590816102e2575b60011490816102d8575b1590816102cf575b506102c05767ffffffffffffffff1981166001175f80516020610f7e8339815191525585610294575b5083151580610282575b80610270575b15610261576101f56101f592610203956101b3610f12565b6101bb610f12565b60ff195f80516020610f5e83398151915254165f80516020610f5e833981519152556001600160601b0360a01b6002541617600255610bec565b506101fe610a7d565b610c9b565b5061020a57005b68ff0000000000000000195f80516020610f7e83398151915254165f80516020610f7e833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b63d92e233d60e01b5f5260045ffd5b506001600160a01b038116151561019b565b506001600160a01b0382161515610195565b68ffffffffffffffffff191668010000000000000001175f80516020610f7e833981519152555f61018b565b63f92ee8a960e01b5f5260045ffd5b9050155f610162565b303b15915061015a565b879150610150565b5f80fd5b346102ea5760a03660031901126102ea5761032d61030a610a45565b61031a610315610a7d565b610ba6565b6084359060643590602435600435610ddb565b005b346102ea575f3660031901126102ea576002546040516001600160a01b039091168152602090f35b346102ea5760403660031901126102ea5761032d600435610376610a2f565b9061038361031582610b39565b610d3f565b346102ea5760a03660031901126102ea5760043567ffffffffffffffff81116102ea576103b9903690600401610ac4565b60243567ffffffffffffffff81116102ea576103d9903690600401610ac4565b6044359167ffffffffffffffff83116102ea57366023840112156102ea57826004013561040581610aac565b936104136040519586610a5b565b8185526024602086019260051b820101903682116102ea57602401915b81831061052d5750505060643567ffffffffffffffff81116102ea5761045a903690600401610ac4565b9160843567ffffffffffffffff81116102ea5761047b903690600401610ac4565b610486610315610a7d565b8251938483511480610523575b80610519575b8061050f575b15610500575f5b8581106104af57005b806104fa6104bf60019388610b57565b516104ca8388610b57565b51848060a01b036104db858d610b57565b51166104e78588610b57565b51916104f3868a610b57565b5193610ddb565b016104a6565b638b13b26360e01b5f5260045ffd5b508482511461049f565b5084815114610499565b5084865114610493565b82356001600160a01b03811681036102ea57815260209283019201610430565b346102ea5760203660031901126102ea576004355f526001602052602060405f2054604051908152f35b346102ea575f3660031901126102ea57335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561063c575f80516020610f5e8339815191525460ff161561062d576105dc610b7f565b600160ff195f80516020610f5e8339815191525416175f80516020610f5e833981519152557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b638dfc202b60e01b5f5260045ffd5b63e2517d3f60e01b5f52336004525f60245260445ffd5b346102ea575f3660031901126102ea5760206040515f8152f35b346102ea575f3660031901126102ea576020610687610a7d565b604051908152f35b346102ea5760403660031901126102ea576106a8610a2f565b6004355f525f80516020610f3e83398151915260205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346102ea575f3660031901126102ea57602060ff5f80516020610f5e83398151915254166040519015158152f35b346102ea5760403660031901126102ea5761072b610a2f565b336001600160a01b038216036107475761032d90600435610d3f565b63334bd91960e11b5f5260045ffd5b346102ea5760403660031901126102ea5761032d600435610775610a2f565b906101fe61031582610b39565b346102ea5760203660031901126102ea576020610687600435610b39565b346102ea5760203660031901126102ea576004355f525f602052608060405f2080549060018060a01b03600182015416906003600282015491015491604051938452602084015260408301526060820152f35b346102ea5760403660031901126102ea57600435602435610812610b7f565b80156109cd576002546040516323b872dd60e01b81523360048201523060248201526044810183905290602090829060649082905f906001600160a01b03165af19081156109c2575f91610993575b5015610984576003545f19811461097057600101918260035560405160208101903360601b8252826034820152836054820152846074820152607481526108a9609482610a5b565b519020916040516080810181811067ffffffffffffffff82111761095c57602095600391604052858352868301338152604084019086825260608501928684525f525f895260405f2094518555600185019060018060a01b039051166001600160601b0360a01b8254161790555160028401555191015560035460405191825284820152827fc2507e9f40fb7ddcc3fecc870160b2dcaf4fadc223535a7e23ca9fcf87c89e1b60403393a4604051908152f35b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b631a67cf2760e31b5f5260045ffd5b6109b5915060203d6020116109bb575b6109ad8183610a5b565b810190610b21565b83610861565b503d6109a3565b6040513d5f823e3d90fd5b631f2a200560e01b5f5260045ffd5b346102ea5760203660031901126102ea576004359063ffffffff60e01b82168092036102ea57602091637965db0b60e01b8114908115610a1e575b5015158152f35b6301ffc9a760e01b14905083610a17565b602435906001600160a01b03821682036102ea57565b604435906001600160a01b03821682036102ea57565b90601f8019910116810190811067ffffffffffffffff82111761095c57604052565b60405160208101906b52454c415945525f524f4c4560a01b8252600c8152610aa6602c82610a5b565b51902090565b67ffffffffffffffff811161095c5760051b60200190565b9080601f830112156102ea578135610adb81610aac565b92610ae96040519485610a5b565b81845260208085019260051b8201019283116102ea57602001905b828210610b115750505090565b8135815260209182019101610b04565b908160209103126102ea575180151581036102ea5790565b5f525f80516020610f3e833981519152602052600160405f20015490565b8051821015610b6b5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b60ff5f80516020610f5e8339815191525416610b9757565b63d93c066560e01b5f5260045ffd5b5f8181525f80516020610f3e8339815191526020908152604080832033845290915290205460ff1615610bd65750565b63e2517d3f60e01b5f523360045260245260445ffd5b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16610c96576001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b5f8181525f80516020610f3e833981519152602090815260408083206001600160a01b038616845290915290205460ff16610d39575f8181525f80516020610f3e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f8181525f80516020610f3e833981519152602090815260408083206001600160a01b038616845290915290205460ff1615610d39575f8181525f80516020610f3e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b91939290928015610f0357825f52600160205260405f2054610ef457604051602081019085825260018060a01b038716966001600160601b03199060601b16604082015283605482015282607482015260748152610e3a609482610a5b565b5190208303610ee557825f5260016020528060405f20555f602060018060a01b036002541660446040518094819363a9059cbb60e01b83528b60048401528860248401525af19081156109c2575f91610ec6575b5015610984577fe8198bb3641f40f04ef209432a2832b3fa23e85c0360473bc30124d0e0cf60cf9160409182519182526020820152a4565b610edf915060203d6020116109bb576109ad8183610a5b565b5f610e8e565b630118b74360e31b5f5260045ffd5b633f968ce760e21b5f5260045ffd5b633ab3447f60e11b5f5260045ffd5b60ff5f80516020610f7e8339815191525460401c1615610f2e57565b631afcd79f60e31b5f5260045ffdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f8d5540fd123e084c158e0fce26252bf97013cfedfc770adc812d57faacc3d6c64736f6c634300081a0033","sourceMap":"476:4415:57:-:0;;;;;;;8837:64:25;476:4415:57;;;;;;7896:76:25;;-1:-1:-1;;;;;;;;;;;476:4415:57;;7985:34:25;7981:146;;-1:-1:-1;476:4415:57;;;;;;;;;7981:146:25;-1:-1:-1;;;;;;476:4415:57;-1:-1:-1;;;;;476:4415:57;;;8837:64:25;476:4415:57;;;8087:29:25;;476:4415:57;;8087:29:25;7981:146;;;;7896:76;7938:23;;;-1:-1:-1;7938:23:25;;-1:-1:-1;7938:23:25;476:4415:57;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146109dc575080630b417df4146107f35780631058648f146107a0578063248a9ca3146107825780632f2ff15d1461075657806336568abe146107125780635c975abb146106e457806391d148541461068f578063926d7d7f1461066d578063a217fddf14610653578063c4ae316814610577578063c509918f1461054d578063ca0c9c2314610388578063d547741f14610357578063e20c95ec1461032f578063e31219ee146102ee5763f8c8765e146100d7575f80fd5b346102ea5760803660031901126102ea576004356001600160a01b038116908190036102ea57610105610a2f565b9061010e610a45565b91606435906001600160a01b03821682036102ea575f80516020610f7e833981519152549360ff8560401c16159467ffffffffffffffff8116801590816102e2575b60011490816102d8575b1590816102cf575b506102c05767ffffffffffffffff1981166001175f80516020610f7e8339815191525585610294575b5083151580610282575b80610270575b15610261576101f56101f592610203956101b3610f12565b6101bb610f12565b60ff195f80516020610f5e83398151915254165f80516020610f5e833981519152556001600160601b0360a01b6002541617600255610bec565b506101fe610a7d565b610c9b565b5061020a57005b68ff0000000000000000195f80516020610f7e83398151915254165f80516020610f7e833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b63d92e233d60e01b5f5260045ffd5b506001600160a01b038116151561019b565b506001600160a01b0382161515610195565b68ffffffffffffffffff191668010000000000000001175f80516020610f7e833981519152555f61018b565b63f92ee8a960e01b5f5260045ffd5b9050155f610162565b303b15915061015a565b879150610150565b5f80fd5b346102ea5760a03660031901126102ea5761032d61030a610a45565b61031a610315610a7d565b610ba6565b6084359060643590602435600435610ddb565b005b346102ea575f3660031901126102ea576002546040516001600160a01b039091168152602090f35b346102ea5760403660031901126102ea5761032d600435610376610a2f565b9061038361031582610b39565b610d3f565b346102ea5760a03660031901126102ea5760043567ffffffffffffffff81116102ea576103b9903690600401610ac4565b60243567ffffffffffffffff81116102ea576103d9903690600401610ac4565b6044359167ffffffffffffffff83116102ea57366023840112156102ea57826004013561040581610aac565b936104136040519586610a5b565b8185526024602086019260051b820101903682116102ea57602401915b81831061052d5750505060643567ffffffffffffffff81116102ea5761045a903690600401610ac4565b9160843567ffffffffffffffff81116102ea5761047b903690600401610ac4565b610486610315610a7d565b8251938483511480610523575b80610519575b8061050f575b15610500575f5b8581106104af57005b806104fa6104bf60019388610b57565b516104ca8388610b57565b51848060a01b036104db858d610b57565b51166104e78588610b57565b51916104f3868a610b57565b5193610ddb565b016104a6565b638b13b26360e01b5f5260045ffd5b508482511461049f565b5084815114610499565b5084865114610493565b82356001600160a01b03811681036102ea57815260209283019201610430565b346102ea5760203660031901126102ea576004355f526001602052602060405f2054604051908152f35b346102ea575f3660031901126102ea57335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561063c575f80516020610f5e8339815191525460ff161561062d576105dc610b7f565b600160ff195f80516020610f5e8339815191525416175f80516020610f5e833981519152557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b638dfc202b60e01b5f5260045ffd5b63e2517d3f60e01b5f52336004525f60245260445ffd5b346102ea575f3660031901126102ea5760206040515f8152f35b346102ea575f3660031901126102ea576020610687610a7d565b604051908152f35b346102ea5760403660031901126102ea576106a8610a2f565b6004355f525f80516020610f3e83398151915260205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346102ea575f3660031901126102ea57602060ff5f80516020610f5e83398151915254166040519015158152f35b346102ea5760403660031901126102ea5761072b610a2f565b336001600160a01b038216036107475761032d90600435610d3f565b63334bd91960e11b5f5260045ffd5b346102ea5760403660031901126102ea5761032d600435610775610a2f565b906101fe61031582610b39565b346102ea5760203660031901126102ea576020610687600435610b39565b346102ea5760203660031901126102ea576004355f525f602052608060405f2080549060018060a01b03600182015416906003600282015491015491604051938452602084015260408301526060820152f35b346102ea5760403660031901126102ea57600435602435610812610b7f565b80156109cd576002546040516323b872dd60e01b81523360048201523060248201526044810183905290602090829060649082905f906001600160a01b03165af19081156109c2575f91610993575b5015610984576003545f19811461097057600101918260035560405160208101903360601b8252826034820152836054820152846074820152607481526108a9609482610a5b565b519020916040516080810181811067ffffffffffffffff82111761095c57602095600391604052858352868301338152604084019086825260608501928684525f525f895260405f2094518555600185019060018060a01b039051166001600160601b0360a01b8254161790555160028401555191015560035460405191825284820152827fc2507e9f40fb7ddcc3fecc870160b2dcaf4fadc223535a7e23ca9fcf87c89e1b60403393a4604051908152f35b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b631a67cf2760e31b5f5260045ffd5b6109b5915060203d6020116109bb575b6109ad8183610a5b565b810190610b21565b83610861565b503d6109a3565b6040513d5f823e3d90fd5b631f2a200560e01b5f5260045ffd5b346102ea5760203660031901126102ea576004359063ffffffff60e01b82168092036102ea57602091637965db0b60e01b8114908115610a1e575b5015158152f35b6301ffc9a760e01b14905083610a17565b602435906001600160a01b03821682036102ea57565b604435906001600160a01b03821682036102ea57565b90601f8019910116810190811067ffffffffffffffff82111761095c57604052565b60405160208101906b52454c415945525f524f4c4560a01b8252600c8152610aa6602c82610a5b565b51902090565b67ffffffffffffffff811161095c5760051b60200190565b9080601f830112156102ea578135610adb81610aac565b92610ae96040519485610a5b565b81845260208085019260051b8201019283116102ea57602001905b828210610b115750505090565b8135815260209182019101610b04565b908160209103126102ea575180151581036102ea5790565b5f525f80516020610f3e833981519152602052600160405f20015490565b8051821015610b6b5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b60ff5f80516020610f5e8339815191525416610b9757565b63d93c066560e01b5f5260045ffd5b5f8181525f80516020610f3e8339815191526020908152604080832033845290915290205460ff1615610bd65750565b63e2517d3f60e01b5f523360045260245260445ffd5b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16610c96576001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b5f8181525f80516020610f3e833981519152602090815260408083206001600160a01b038616845290915290205460ff16610d39575f8181525f80516020610f3e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f8181525f80516020610f3e833981519152602090815260408083206001600160a01b038616845290915290205460ff1615610d39575f8181525f80516020610f3e833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b91939290928015610f0357825f52600160205260405f2054610ef457604051602081019085825260018060a01b038716966001600160601b03199060601b16604082015283605482015282607482015260748152610e3a609482610a5b565b5190208303610ee557825f5260016020528060405f20555f602060018060a01b036002541660446040518094819363a9059cbb60e01b83528b60048401528860248401525af19081156109c2575f91610ec6575b5015610984577fe8198bb3641f40f04ef209432a2832b3fa23e85c0360473bc30124d0e0cf60cf9160409182519182526020820152a4565b610edf915060203d6020116109bb576109ad8183610a5b565b5f610e8e565b630118b74360e31b5f5260045ffd5b633f968ce760e21b5f5260045ffd5b633ab3447f60e11b5f5260045ffd5b60ff5f80516020610f7e8339815191525460401c1615610f2e57565b631afcd79f60e31b5f5260045ffdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f8d5540fd123e084c158e0fce26252bf97013cfedfc770adc812d57faacc3d6c64736f6c634300081a0033","sourceMap":"476:4415:57:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;4301:16:25;476:4415:57;;;;4726:16:25;;:34;;;;476:4415:57;4805:1:25;4790:16;:50;;;;476:4415:57;4855:13:25;:30;;;;476:4415:57;4851:91:25;;;-1:-1:-1;;476:4415:57;;4805:1:25;476:4415:57;-1:-1:-1;;;;;;;;;;;476:4415:57;;4979:67:25;;476:4415:57;1318:24;;;;:48;;;476:4415;1318:74;;;476:4415;;;;1485:38;1533:34;6893:76:25;1612:37:57;6893:76:25;;;:::i;:::-;;;:::i;:::-;476:4415:57;;-1:-1:-1;;;;;;;;;;;476:4415:57;;-1:-1:-1;;;;;;;;;;;476:4415:57;-1:-1:-1;;;;;476:4415:57;;1445:30;476:4415;;;1445:30;476:4415;1485:38;:::i;:::-;;1544:12;;:::i;:::-;1533:34;:::i;1612:37::-;;5066:101:25;;476:4415:57;5066:101:25;476:4415:57;;-1:-1:-1;;;;;;;;;;;476:4415:57;;-1:-1:-1;;;;;;;;;;;476:4415:57;5142:14:25;476:4415:57;;;4805:1:25;476:4415:57;;5142:14:25;476:4415:57;;;;;;;;;;1318:74;-1:-1:-1;;;;;;476:4415:57;;1370:22;;1318:74;;:48;-1:-1:-1;;;;;;476:4415:57;;1346:20;;1318:48;;4979:67:25;-1:-1:-1;;476:4415:57;;;-1:-1:-1;;;;;;;;;;;476:4415:57;4979:67:25;;;4851:91;4908:23;;;476:4415:57;4908:23:25;476:4415:57;;4908:23:25;4855:30;4872:13;;;4855:30;;;4790:50;4818:4;4810:25;:30;;-1:-1:-1;4790:50:25;;4726:34;;;-1:-1:-1;4726:34:25;;476:4415:57;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;2914:5;476:4415;;:::i;:::-;3202:4:24;2818:12:57;;:::i;:::-;3202:4:24;:::i;:::-;476:4415:57;;;;;;;;;;2914:5;:::i;:::-;476:4415;;;;;;;-1:-1:-1;;476:4415:57;;;;882:23;476:4415;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;5767:26:24;476:4415:57;;;;:::i;:::-;5737:18:24;3202:4;5737:18;;;:::i;3202:4::-;5767:26;:::i;476:4415:57:-;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;3202:4:24;3179:12:57;;:::i;3202:4:24:-;476:4415:57;;;;;;3330:27;:58;;;476:4415;3330:86;;;476:4415;3330:129;;;476:4415;;;;;3593:10;;;;;;476:4415;3605:3;3648:20;3712:9;3648:20;476:4415;3648:20;;;:::i;:::-;476:4415;3670:13;;;;:::i;:::-;476:4415;;;;;;3685:13;;;;:::i;:::-;476:4415;;3700:10;;;;:::i;:::-;476:4415;3712:9;;;;;:::i;:::-;476:4415;3712:9;;:::i;:::-;476:4415;3582:9;;476:4415;;;;;;;;;3330:129;476:4415;;;;3436:23;3330:129;;:86;476:4415;;;;3392:24;3330:86;;:58;476:4415;;;;3361:27;3330:58;;476:4415;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;966:10:27;476:4415:57;;;;;;;;;;;;;4468:23:24;4464:108;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;2281:72:28;;:::i;:::-;3496:4;476:4415:57;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;-1:-1:-1;;;;;;;;;;;476:4415:57;3515:20:28;476:4415:57;;;966:10:27;476:4415:57;;3515:20:28;476:4415:57;4850:32;3199:15:28;;;476:4415:57;3199:15:28;476:4415:57;;3199:15:28;4464:108:24;4514:47;;;476:4415:57;4514:47:24;966:10:27;476:4415:57;;;;;;;4514:47:24;476:4415:57;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;911:82;;:::i;:::-;476:4415;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;3901:31:24;476:4415:57;;;;;;-1:-1:-1;476:4415:57;;;;;;-1:-1:-1;476:4415:57;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;:::i;:::-;966:10:27;-1:-1:-1;;;;;476:4415:57;;6441:34:24;6437:102;;6549:37;476:4415:57;;;6549:37:24;:::i;6437:102::-;6498:30;;;476:4415:57;6498:30:24;476:4415:57;;6498:30:24;476:4415:57;;;;;;-1:-1:-1;;476:4415:57;;;;5350:25:24;476:4415:57;;;;:::i;:::-;5320:18:24;3202:4;5320:18;;;:::i;476:4415:57:-;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;;;;;;;;;;;;;;;;717:75;;476:4415;;717:75;;;;;476:4415;717:75;;476:4415;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;2281:72:28;;:::i;:::-;1875:10:57;;476:4415;;2059:9;476:4415;;;-1:-1:-1;;;2059:56:57;;1967:10;476:4415;2059:56;;476:4415;2101:4;476:4415;;;;;;;;;;;;;;;2059:56;;476:4415;;;;-1:-1:-1;;;;;476:4415:57;2059:56;;;;;;;476:4415;2059:56;;;476:4415;2058:57;;2054:90;;2320:8;476:4415;-1:-1:-1;;476:4415:57;;;;;;;;2320:8;476:4415;;;;2273:56;;1967:10;;476:4415;;;;;;;;;;;;;;;;;;;;2273:56;;;;;;:::i;:::-;476:4415;2263:67;;476:4415;;;;;;;;;;;;;;;;;2320:8;476:4415;;;;;;2422:64;;;1967:10;476:4415;;;2422:64;;476:4415;;;;;2422:64;;476:4415;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;2059:9;476:4415;;;;;;;2320:8;476:4415;;;;;;;;;;1967:10;2502:79;476:4415;1967:10;2502:79;;476:4415;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2054:90;2124:20;;;476:4415;2124:20;476:4415;;2124:20;2059:56;;;;476:4415;2059:56;476:4415;2059:56;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;476:4415;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;3551:47:24;;;:87;;;;476:4415:57;;;;;;;3551:87:24;-1:-1:-1;;;1134:40:29;;-1:-1:-1;3551:87:24;;;476:4415:57;;;;-1:-1:-1;;;;;476:4415:57;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;476:4415:57;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;:::o;911:82::-;476:4415;;960:32;;;476:4415;-1:-1:-1;;;476:4415:57;;960:32;;;;;;;:::i;:::-;476:4415;950:43;;911:82;:::o;476:4415::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;4759:191:24:-;-1:-1:-1;476:4415:57;-1:-1:-1;;;;;;;;;;;476:4415:57;;4919:24:24;476:4415:57;-1:-1:-1;476:4415:57;4919:24:24;476:4415:57;4759:191:24;:::o;476:4415:57:-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;2905:128:28;476:4415:57;-1:-1:-1;;;;;;;;;;;476:4415:57;;2966:61:28;;2905:128::o;2966:61::-;3001:15;;;-1:-1:-1;3001:15:28;;-1:-1:-1;3001:15:28;4148:103:24;-1:-1:-1;476:4415:57;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;966:10:27;476:4415:57;;;;;;;;;;4468:23:24;4464:108;;4148:103;:::o;4464:108::-;4514:47;;;-1:-1:-1;4514:47:24;966:10:27;4514:47:24;476:4415:57;;;;-1:-1:-1;4514:47:24;7270:387;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;966:10:27;;476:4415:57;7532:40:24;476:4415:57;;7532:40:24;7509:4;7586:11;:::o;7432:219::-;7628:12;476:4415:57;7628:12:24;:::o;7270:387::-;476:4415:57;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;;;966:10:27;;476:4415:57;;7532:40:24;;476:4415:57;7532:40:24;7509:4;7586:11;:::o;7432:219::-;7628:12;;476:4415:57;7628:12:24;:::o;7892:388::-;476:4415:57;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;-1:-1:-1;;476:4415:57;;;966:10:27;;476:4415:57;;8155:40:24;;476:4415:57;8155:40:24;476:4415:57;8209:11:24;:::o;3745:1028:57:-;;;;;;4050:9;;476:4415;;;4058:1;476:4415;4094:19;476:4415;;;4058:1;476:4415;;;;;;;4329:53;;476:4415;;;;;;;;;;;;-1:-1:-1;;;;;476:4415:57;;;;;;;;;;;;;;;;;;;;4329:53;;;;;;:::i;:::-;476:4415;4319:64;;4299:84;;476:4415;;;4058:1;476:4415;4094:19;476:4415;;;;4058:1;476:4415;;4058:1;476:4415;;;;;;4606:9;476:4415;;4606:37;476:4415;;;;;;;;;4606:37;;;;;;476:4415;;;;;;4606:37;;;;;;;4058:1;4606:37;;;3745:1028;4605:38;;4601:71;;4688:78;476:4415;;;;;;;;;;;;4688:78;3745:1028::o;4606:37::-;;;;476:4415;4606:37;476:4415;4606:37;;;;;;;:::i;:::-;;;;476:4415;;;;4058:1;476:4415;;4058:1;476:4415;;;;;4058:1;476:4415;;4058:1;476:4415;;;;;4058:1;476:4415;;4058:1;476:4415;7084:141:25;476:4415:57;-1:-1:-1;;;;;;;;;;;476:4415:57;;;;7150:18:25;7146:73;;7084:141::o;7146:73::-;7191:17;;;-1:-1:-1;7191:17:25;;-1:-1:-1;7191:17:25","linkReferences":{}},"methodIdentifiers":{"DEFAULT_ADMIN_ROLE()":"a217fddf","RELAYER_ROLE()":"926d7d7f","batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])":"ca0c9c23","completeBridgeTransfer(bytes32,bytes32,address,uint256,uint256)":"e31219ee","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","idsToIncomingNonces(bytes32)":"c509918f","initialize(address,address,address,address)":"f8c8765e","initiateBridgeTransfer(bytes32,uint256)":"0b417df4","moveToken()":"e20c95ec","noncesToOutgoingTransfers(uint256)":"1058648f","paused()":"5c975abb","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","togglePause()":"c4ae3168"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CompletedBridgeTransferId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBridgeTransferId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidLenghts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MOVETransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAmount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originator\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"originator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"BridgeTransferInitiated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RELAYER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"bridgeTransferIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"initiators\",\"type\":\"bytes32[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"nonces\",\"type\":\"uint256[]\"}],\"name\":\"batchCompleteBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"initiator\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"completeBridgeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"name\":\"idsToIncomingNonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_moveToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_relayer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_maintainer\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initiateBridgeTransfer\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"moveToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"noncesToOutgoingTransfers\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"bridgeTransferId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"togglePause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"EnforcedPause()\":[{\"details\":\"The operation failed because the contract is paused.\"}],\"ExpectedPause()\":[{\"details\":\"The operation failed because the contract is not paused.\"}],\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}]},\"events\":{\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"},\"Paused(address)\":{\"details\":\"Emitted when the pause is triggered by `account`.\"},\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted signaling this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"},\"Unpaused(address)\":{\"details\":\"Emitted when the pause is lifted by `account`.\"}},\"kind\":\"dev\",\"methods\":{\"batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])\":{\"details\":\"Completes multiple bridge transfers\",\"params\":{\"amounts\":\"The amounts to transfer\",\"bridgeTransferIds\":\"Unique identifiers for the BridgeTransfers\",\"initiators\":\"The addresses on the other chain that originated the transfer of funds\",\"nonces\":\"The seed nonces to generate the bridgeTransferIds\",\"recipients\":\"The addresses on this chain to which to transfer funds\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"initiateBridgeTransfer(bytes32,uint256)\":{\"details\":\"Creates a new bridge\",\"params\":{\"amount\":\"The amount of MOVE to send\",\"recipient\":\"The address on the other chain to which to transfer funds\"},\"returns\":{\"bridgeTransferId\":\"A unique id representing this BridgeTransfer\"}},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"See {IERC165-supportsInterface}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/NativeBridge.sol\":\"NativeBridge\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"],\"viaIR\":true},\"sources\":{\"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol\":{\"keccak256\":\"0x6662ec4e5cefca03eeadd073e9469df8d2944bb2ee8ec8f7622c2c46aab5f225\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4d8544c6f8daa4d1bc215c6a72fe0acdb748664a105b0e5efc19295667521d45\",\"dweb:/ipfs/QmdGWqdnXT8S3RgCR6aV8XHZrsybieMQLLnug1NtpSjEXN\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol\":{\"keccak256\":\"0x92915b7f7f642c6be3f65bfd1522feb5d5b6ef25f755f4dbb51df32c868f2f97\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://85ad36d5cc7e190e1ee6c94b24659bc3a31396c4c36b6ffa6a509e10661f8007\",\"dweb:/ipfs/QmPFyc4zMh2zo6YWZt25gjm3YdR2hg6wGETaWw256fMmJJ\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol\":{\"keccak256\":\"0xddfe0afa85367153020524d383fe0bc9a1545f343019ddf33f98f392887047f0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://62f3df350ce83190e81673e424dd47d9ee8ffb121a6f72994c75c8ae3fc9dded\",\"dweb:/ipfs/QmaA2CMuqESmdgVao4XzCL4aRwcZW1xjUXpY7RqHZfQoAV\"]},\"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60\",\"dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009\",\"dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e\",\"dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX\"]},\"src/INativeBridge.sol\":{\"keccak256\":\"0xb5789d8b797212be0c13d2361759b14b257765c7c6056c4513fc686fb9ba6cd3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4348206c5d8e1d5483975cad134dc3aa3c15b2473218a59121e028a4eaa2dbf2\",\"dweb:/ipfs/QmTWAWkbxa18y53brmzWafqNXt5siNyaajd5U3C7kh8pna\"]},\"src/NativeBridge.sol\":{\"keccak256\":\"0xdf60787f8930f66c52a1bc357ca243ab2bbbfc33d4a78d30b7ecc2b131f73c47\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://4d4167b80d26a6e52f2e685321c216992dd5dac13bcc18e97f098fb42f49f309\",\"dweb:/ipfs/QmVYpRZZvFHw2deBXKtraP9KmyfiBdcUNnBMCCTbxZa7m8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.26+commit.8a97fa7a"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[],"type":"error","name":"CompletedBridgeTransferId"},{"inputs":[],"type":"error","name":"EnforcedPause"},{"inputs":[],"type":"error","name":"ExpectedPause"},{"inputs":[],"type":"error","name":"InvalidBridgeTransferId"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"InvalidLenghts"},{"inputs":[],"type":"error","name":"InvalidNonce"},{"inputs":[],"type":"error","name":"MOVETransferFailed"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[],"type":"error","name":"ZeroAddress"},{"inputs":[],"type":"error","name":"ZeroAmount"},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"originator","type":"bytes32","indexed":true},{"internalType":"address","name":"recipient","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint256","name":"nonce","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferCompleted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32","indexed":true},{"internalType":"address","name":"originator","type":"address","indexed":true},{"internalType":"bytes32","name":"recipient","type":"bytes32","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint256","name":"nonce","type":"uint256","indexed":false}],"type":"event","name":"BridgeTransferInitiated","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"account","type":"address","indexed":false}],"type":"event","name":"Paused","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[{"internalType":"address","name":"account","type":"address","indexed":false}],"type":"event","name":"Unpaused","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"RELAYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32[]","name":"bridgeTransferIds","type":"bytes32[]"},{"internalType":"bytes32[]","name":"initiators","type":"bytes32[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"nonces","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function","name":"batchCompleteBridgeTransfer"},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"bytes32","name":"initiator","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"completeBridgeTransfer"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}],"stateMutability":"view","type":"function","name":"idsToIncomingNonces","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"_moveToken","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_relayer","type":"address"},{"internalType":"address","name":"_maintainer","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"initialize"},{"inputs":[{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"initiateBridgeTransfer","outputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"moveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function","name":"noncesToOutgoingTransfers","outputs":[{"internalType":"bytes32","name":"bridgeTransferId","type":"bytes32"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"togglePause"}],"devdoc":{"kind":"dev","methods":{"batchCompleteBridgeTransfer(bytes32[],bytes32[],address[],uint256[],uint256[])":{"details":"Completes multiple bridge transfers","params":{"amounts":"The amounts to transfer","bridgeTransferIds":"Unique identifiers for the BridgeTransfers","initiators":"The addresses on the other chain that originated the transfer of funds","nonces":"The seed nonces to generate the bridgeTransferIds","recipients":"The addresses on this chain to which to transfer funds"}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"initiateBridgeTransfer(bytes32,uint256)":{"details":"Creates a new bridge","params":{"amount":"The amount of MOVE to send","recipient":"The address on the other chain to which to transfer funds"},"returns":{"bridgeTransferId":"A unique id representing this BridgeTransfer"}},"paused()":{"details":"Returns true if the contract is paused, and false otherwise."},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"See {IERC165-supportsInterface}."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/NativeBridge.sol":"NativeBridge"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol":{"keccak256":"0x6662ec4e5cefca03eeadd073e9469df8d2944bb2ee8ec8f7622c2c46aab5f225","urls":["bzz-raw://4d8544c6f8daa4d1bc215c6a72fe0acdb748664a105b0e5efc19295667521d45","dweb:/ipfs/QmdGWqdnXT8S3RgCR6aV8XHZrsybieMQLLnug1NtpSjEXN"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol":{"keccak256":"0x92915b7f7f642c6be3f65bfd1522feb5d5b6ef25f755f4dbb51df32c868f2f97","urls":["bzz-raw://85ad36d5cc7e190e1ee6c94b24659bc3a31396c4c36b6ffa6a509e10661f8007","dweb:/ipfs/QmPFyc4zMh2zo6YWZt25gjm3YdR2hg6wGETaWw256fMmJJ"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol":{"keccak256":"0xddfe0afa85367153020524d383fe0bc9a1545f343019ddf33f98f392887047f0","urls":["bzz-raw://62f3df350ce83190e81673e424dd47d9ee8ffb121a6f72994c75c8ae3fc9dded","dweb:/ipfs/QmaA2CMuqESmdgVao4XzCL4aRwcZW1xjUXpY7RqHZfQoAV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol":{"keccak256":"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d","urls":["bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60","dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4","urls":["bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009","dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"keccak256":"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c","urls":["bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e","dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX"],"license":"MIT"},"src/INativeBridge.sol":{"keccak256":"0xb5789d8b797212be0c13d2361759b14b257765c7c6056c4513fc686fb9ba6cd3","urls":["bzz-raw://4348206c5d8e1d5483975cad134dc3aa3c15b2473218a59121e028a4eaa2dbf2","dweb:/ipfs/QmTWAWkbxa18y53brmzWafqNXt5siNyaajd5U3C7kh8pna"],"license":"MIT"},"src/NativeBridge.sol":{"keccak256":"0xdf60787f8930f66c52a1bc357ca243ab2bbbfc33d4a78d30b7ecc2b131f73c47","urls":["bzz-raw://4d4167b80d26a6e52f2e685321c216992dd5dac13bcc18e97f098fb42f49f309","dweb:/ipfs/QmVYpRZZvFHw2deBXKtraP9KmyfiBdcUNnBMCCTbxZa7m8"],"license":"UNLICENSED"}},"version":1},"id":57} \ No newline at end of file diff --git a/protocol-units/bridge/service/bin/start_indexer.rs b/protocol-units/bridge/service/bin/start_indexer.rs new file mode 100644 index 000000000..962664fd2 --- /dev/null +++ b/protocol-units/bridge/service/bin/start_indexer.rs @@ -0,0 +1,83 @@ +use anyhow::Result; +use bridge_config::Config; +use bridge_indexer_db::run_indexer_client; +use bridge_service::chains::ethereum::event_monitoring::EthMonitoring; +use bridge_service::chains::movement::event_monitoring::MovementMonitoring; +use bridge_service::rest::BridgeRest; +use bridge_util::chains::check_monitoring_health; +use godfig::{backend::config_file::ConfigFile, Godfig}; + +#[tokio::main] +async fn main() -> Result<()> { + use tracing_subscriber::EnvFilter; + + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), + ) + .init(); + + tracing::info!("Start Bridge"); + + // Define bridge config path + let mut dot_movement = dot_movement::DotMovement::try_from_env()?; + let pathbuff = bridge_config::get_config_path(&dot_movement); + dot_movement.set_path(pathbuff); + + let config_file = dot_movement.try_get_or_create_config_file().await?; + + // Get a matching godfig object + let godfig: Godfig = Godfig::new(ConfigFile::new(config_file), vec![]); + let bridge_config: Config = godfig.try_wait_for_ready().await?; + + tracing::info!("Bridge config loaded: {bridge_config:?}"); + + let (eth_client_health_tx, eth_client_health_rx) = tokio::sync::mpsc::channel(10); + let (mvt_client_health_tx, mvt_client_health_rx) = tokio::sync::mpsc::channel(10); + let eth_stream = EthMonitoring::build(&bridge_config.eth, eth_client_health_rx).await.unwrap(); + let mvt_stream = MovementMonitoring::build(&bridge_config.movement, mvt_client_health_rx) + .await + .unwrap(); + + // Initialize the Rpc health check service + let (eth_rest_health_tx, eth_rest_health_rx) = tokio::sync::mpsc::channel(10); + let (mvt_rest_health_tx, mvt_rest_health_rx) = tokio::sync::mpsc::channel(10); + // Create and run the REST service + let url = format!( + "{}:{}", + bridge_config.indexer.rest_listener_hostname, bridge_config.indexer.rest_port + ); + let rest_service = BridgeRest::new(url, eth_rest_health_tx, mvt_rest_health_tx)?; + let rest_service_future = rest_service.run_service(); + let rest_jh = tokio::spawn(rest_service_future); + + tracing::info!("Bridge Eth and Movement Inited. Starting bridge loop."); + + // Start Monitoring health check. + let eth_healh_check_jh = + tokio::spawn(check_monitoring_health("Eth", eth_client_health_tx, eth_rest_health_rx)); + let mvt_healh_check_jh = + tokio::spawn(check_monitoring_health("Mvt", mvt_client_health_tx, mvt_rest_health_rx)); + + tracing::info!("Bridge Eth and Movement Inited. Starting bridge loop."); + + // Start indexer + let indexer_jh = tokio::spawn(run_indexer_client(bridge_config, eth_stream, mvt_stream, None)); + + tokio::select! { + res = eth_healh_check_jh => { + tracing::error!("Heath check Eth monitoring exit because :{res:?}"); + } + res = mvt_healh_check_jh => { + tracing::error!("Heath check Eth monitoring exit because :{res:?}"); + } + res = rest_jh => { + tracing::error!("Heath check Rest server exit because :{res:?}"); + } + res = indexer_jh => { + tracing::error!("Indexer loop exit because :{res:?}"); + } + }; + + Ok(()) +} diff --git a/protocol-units/bridge/service/src/actions.rs b/protocol-units/bridge/service/src/actions.rs index e2a811f5b..516092795 100644 --- a/protocol-units/bridge/service/src/actions.rs +++ b/protocol-units/bridge/service/src/actions.rs @@ -1,6 +1,7 @@ -use crate::chains::movement::utils as movement_utils; -use bridge_util::chains::bridge_contracts::BridgeContract; +//use crate::chains::movement::utils as movement_utils; +use crate::runtime::Runtime; use bridge_util::chains::bridge_contracts::BridgeContractError; +use bridge_util::chains::bridge_contracts::BridgeRelayerContract; use bridge_util::types::BridgeAddress; use bridge_util::ActionExecError; use bridge_util::TransferAction; @@ -10,59 +11,75 @@ use std::pin::Pin; pub fn process_action( action: TransferAction, - mut client: impl BridgeContract + 'static, + state_runtime: &mut Runtime, + mut client: impl BridgeRelayerContract + 'static, ) -> Option> + Send>>> where A: Clone + Send + TryFrom>, { tracing::info!("Action: creating execution for action:{action}"); match action.kind.clone() { - TransferActionType::LockBridgeTransfer { + TransferActionType::CompleteBridgeTransfer { bridge_transfer_id, - hash_lock, initiator, recipient, amount, + nonce, } => { let future = async move { - if recipient.0.len() == 32 { - if let Err(e) = movement_utils::fund_recipient(&recipient).await { - return Err(ActionExecError(action.clone(), e)); - } - } - tracing::info!("Before client.lock_bridge_transfer"); client - .lock_bridge_transfer( + .complete_bridge_transfer( bridge_transfer_id, - hash_lock, initiator, BridgeAddress(recipient.0.try_into().map_err(|_| { ActionExecError( action.clone(), - BridgeContractError::BadAddressEncoding("lock bridge transfer fail to convert recipient address to vec".to_string()), + BridgeContractError::BadAddressEncoding("Complete bridge transfer fail to convert recipient address to vec".to_string()), ) })?), amount, + nonce, ) .await .map_err(|err| ActionExecError(action, err)) }; Some(Box::pin(future)) } - TransferActionType::WaitAndCompleteInitiator(wait_time_sec, secret) => { + TransferActionType::AbortedReplay { + bridge_transfer_id, + initiator, + recipient, + amount, + nonce, + wait_time_sec, + } => { let future = async move { if wait_time_sec != 0 { - let _ = tokio::time::sleep(tokio::time::Duration::from_secs(wait_time_sec)); + let _ = + tokio::time::sleep(tokio::time::Duration::from_secs(wait_time_sec)).await; } client - .initiator_complete_bridge_transfer(action.transfer_id, secret) + .complete_bridge_transfer( + bridge_transfer_id, + initiator, + BridgeAddress(recipient.0.try_into().map_err(|_| { + ActionExecError( + action.clone(), + BridgeContractError::BadAddressEncoding("lock bridge transfer fail to convert recipient address to vec".to_string()), + ) + })?), + amount, + nonce, + ) .await .map_err(|err| ActionExecError(action, err)) }; Some(Box::pin(future)) } - TransferActionType::RefundInitiator => None, - TransferActionType::TransferDone => None, + TransferActionType::CompletedRemoveState => { + state_runtime.remove_transfer(action.transfer_id); + None + } TransferActionType::NoAction => None, } } diff --git a/protocol-units/bridge/service/src/chains/ethereum/client.rs b/protocol-units/bridge/service/src/chains/ethereum/client.rs index e4a4ad0d0..bc8018cd7 100644 --- a/protocol-units/bridge/service/src/chains/ethereum/client.rs +++ b/protocol-units/bridge/service/src/chains/ethereum/client.rs @@ -1,7 +1,4 @@ -use super::types::{ - AlloyProvider, AssetKind, AtomicBridgeCounterpartyMOVE, AtomicBridgeInitiatorMOVE, - CounterpartyContract, EthAddress, InitiatorContract, -}; +use super::types::{AlloyProvider, AssetKind, EthAddress, NativeBridge, NativeBridgeContract}; use super::utils::{calculate_storage_slot, send_transaction, send_transaction_rules}; use alloy::{ network::EthereumWallet, @@ -10,18 +7,17 @@ use alloy::{ rlp::{RlpDecodable, RlpEncodable}, signers::local::PrivateKeySigner, }; -use alloy_primitives::Uint; use alloy_rlp::Decodable; use bridge_config::common::eth::EthConfig; use bridge_grpc::bridge_server::BridgeServer; -use bridge_util::chains::bridge_contracts::{BridgeContractError, BridgeContractResult}; -use bridge_util::types::{ - Amount, BridgeAddress, BridgeTransferDetails, BridgeTransferDetailsCounterparty, - BridgeTransferId, HashLock, HashLockPreImage, TimeLock, +use bridge_util::chains::bridge_contracts::BridgeTransferInitiatedDetails; +use bridge_util::chains::bridge_contracts::{ + BridgeClientContract, BridgeContractError, BridgeContractResult, BridgeRelayerContract, }; +use bridge_util::types::Nonce; +use bridge_util::types::{Amount, BridgeAddress, BridgeTransferId}; use std::{fmt::Debug, net::SocketAddr}; use tonic::transport::Server; -use tracing::info; use url::Url; /// Configuration for the Ethereum Bridge Client @@ -29,8 +25,7 @@ use url::Url; pub struct Config { pub rpc_url: Url, pub signer_private_key: PrivateKeySigner, - pub initiator_contract: Address, - pub counterparty_contract: Address, + pub native_contract: Address, pub movetoken_contract: Address, pub gas_limit: u128, pub transaction_send_retries: u32, @@ -46,8 +41,7 @@ impl TryFrom<&EthConfig> for Config { Ok(Config { rpc_url, signer_private_key, - initiator_contract: conf.eth_initiator_contract.parse()?, - counterparty_contract: conf.eth_counterparty_contract.parse()?, + native_contract: conf.eth_native_contract.parse()?, movetoken_contract: conf.eth_move_token_contract.parse()?, gas_limit: conf.gas_limit.into(), transaction_send_retries: conf.transaction_send_retries, @@ -57,36 +51,31 @@ impl TryFrom<&EthConfig> for Config { } #[derive(RlpDecodable, RlpEncodable)] -struct EthBridgeTransferDetails { +struct EthBridgeTransferDetailsInitiate { pub amount: U256, pub originator: EthAddress, pub recipient: [u8; 32], - pub hash_lock: [u8; 32], - pub time_lock: U256, - pub state: u8, + pub nonce: U256, } #[derive(RlpDecodable, RlpEncodable)] -struct EthBridgeTransferDetailsCounterparty { +struct EthBridgeTransferDetailsComplete { pub amount: U256, pub originator: [u8; 32], pub recipient: EthAddress, - pub hash_lock: [u8; 32], - pub time_lock: U256, - pub state: u8, + pub nonce: U256, } #[derive(Clone)] pub struct EthClient { pub rpc_provider: AlloyProvider, - initiator_contract: InitiatorContract, - counterparty_contract: CounterpartyContract, + native_bridge_contract: NativeBridgeContract, pub config: Config, signer_address: Address, } impl EthClient { - pub async fn new(config: &EthConfig) -> Result { + pub async fn build_with_config(config: &EthConfig) -> Result { let config: Config = config.try_into()?; let signer_address = config.signer_private_key.address(); let rpc_provider = ProviderBuilder::new() @@ -95,15 +84,35 @@ impl EthClient { .on_builtin(config.rpc_url.as_str()) .await?; - let initiator_contract = - AtomicBridgeInitiatorMOVE::new(config.initiator_contract, rpc_provider.clone()); - let counterparty_contract = - AtomicBridgeCounterpartyMOVE::new(config.counterparty_contract, rpc_provider.clone()); + let native_bridge_contract = + NativeBridgeContract::new(config.native_contract, rpc_provider.clone()); + + Ok(EthClient { + rpc_provider, + native_bridge_contract, + config: config.clone(), + signer_address, + }) + } + pub async fn build_with_signer( + signer: PrivateKeySigner, + config: &EthConfig, + ) -> Result { + let mut config: Config = config.try_into()?; + config.signer_private_key = signer; + let signer_address = config.signer_private_key.address(); + let rpc_provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(EthereumWallet::from(config.signer_private_key.clone())) + .on_builtin(config.rpc_url.as_str()) + .await?; + + let native_bridge_contract = + NativeBridgeContract::new(config.native_contract, rpc_provider.clone()); Ok(EthClient { rpc_provider, - initiator_contract, - counterparty_contract, + native_bridge_contract, config: config.clone(), signer_address, }) @@ -124,33 +133,6 @@ impl EthClient { Ok(()) } - pub async fn initialize_counterparty_contract( - &self, - initiator_address: Address, - contract_address: Address, - timelock: TimeLock, - ) -> Result<(), anyhow::Error> { - // Create the counterparty contract instance - let contract = - AtomicBridgeCounterpartyMOVE::new(contract_address, self.rpc_provider.clone()); - - // Prepare the initialize transaction - let call = - contract.initialize(self.signer_address, initiator_address, U256::from(timelock.0)); - - // Send the transaction - send_transaction( - call.to_owned(), - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await?; - - Ok(()) - } - pub async fn get_block_number(&self) -> Result { self.rpc_provider .get_block_number() @@ -162,29 +144,20 @@ impl EthClient { self.config.signer_private_key.address() } - pub fn set_initiator_contract(&mut self, contract: InitiatorContract) { - self.initiator_contract = contract; - } - - pub fn initiator_contract_address(&self) -> Address { - self.config.initiator_contract + pub fn set_initiator_contract(&mut self, contract: NativeBridgeContract) { + self.native_bridge_contract = contract; } - pub fn counterparty_contract_address(&self) -> Address { - self.config.counterparty_contract + pub fn native_contract_address(&self) -> Address { + self.config.native_contract } } #[async_trait::async_trait] -impl bridge_util::chains::bridge_contracts::BridgeContract for EthClient { - // `_initiator`, or in the contract, `originator` is set - // via the `msg.sender`, which is stored in the `rpc_provider`. - // So `initiator` arg is not used here. +impl BridgeClientContract for EthClient { async fn initiate_bridge_transfer( &mut self, - initiator: BridgeAddress, recipient: BridgeAddress>, - hash_lock: HashLock, amount: Amount, // the ETH amount ) -> BridgeContractResult<()> { let recipient_bytes: [u8; 32] = recipient.0.try_into().map_err(|e| { @@ -192,17 +165,11 @@ impl bridge_util::chains::bridge_contracts::BridgeContract for EthCl "Failed to convert in [u8; 32] recipient: {e:?}" )) })?; - let contract = AtomicBridgeInitiatorMOVE::new( - self.config.initiator_contract, - self.rpc_provider.clone(), - ); + let contract = NativeBridge::new(self.config.native_contract, self.rpc_provider.clone()); let call = contract - .initiateBridgeTransfer( - U256::from(amount.0), - FixedBytes(recipient_bytes), - FixedBytes(hash_lock.0), - ) - .from(*initiator.0); + .initiateBridgeTransfer(FixedBytes(recipient_bytes), U256::from(amount.0)) + .from(self.signer_address); + let _ = send_transaction( call, self.signer_address, @@ -218,172 +185,59 @@ impl bridge_util::chains::bridge_contracts::BridgeContract for EthCl Ok(()) } - async fn initiator_complete_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - pre_image: HashLockPreImage, - ) -> BridgeContractResult<()> { - // The Alloy generated type for smart contract`pre_image` arg is `FixedBytes<32>` - // so it must be converted to `[u8; 32]`. - let generic_error = |desc| BridgeContractError::GenericError(String::from(desc)); - let pre_image: [u8; 32] = pre_image - .0 - .get(0..32) - .ok_or(generic_error("Could not get required slice from pre-image"))? - .try_into() - .map_err(|_| generic_error("Could not convert pre-image to [u8; 32]"))?; - info! {"Pre-image: {:?}", pre_image}; - let contract = AtomicBridgeInitiatorMOVE::new( - self.config.initiator_contract, - self.rpc_provider.clone(), - ); - let call = contract - .completeBridgeTransfer(FixedBytes(bridge_transfer_id.0), FixedBytes(pre_image)); - send_transaction( - call, - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await - .map_err(|e| { - BridgeContractError::OnChainError(format!("Failed to send transaction: {}", e)) - })?; - - Ok(()) - } - - async fn counterparty_complete_bridge_transfer( + async fn get_bridge_transfer_details( &mut self, bridge_transfer_id: BridgeTransferId, - pre_image: HashLockPreImage, - ) -> BridgeContractResult<()> { - // The Alloy generated type for smart contract`pre_image` arg is `FixedBytes<32>` - // so it must be converted to `[u8; 32]`. + ) -> BridgeContractResult>> { let generic_error = |desc| BridgeContractError::GenericError(String::from(desc)); - let pre_image: [u8; 32] = pre_image - .0 - .get(0..32) - .ok_or(generic_error("Could not get required slice from pre-image"))? - .try_into() - .map_err(|_| generic_error("Could not convert pre-image to [u8; 32]"))?; - - let contract = AtomicBridgeCounterpartyMOVE::new( - self.config.counterparty_contract, - self.rpc_provider.clone(), - ); - - let call = contract - .completeBridgeTransfer(FixedBytes(bridge_transfer_id.0), FixedBytes(pre_image)); - send_transaction( - call, - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await - .map_err(|e| { - BridgeContractError::OnChainError(format!("Failed to send transaction: {}", e)) - })?; - - Ok(()) - } - async fn refund_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()> { - let contract = AtomicBridgeInitiatorMOVE::new( - self.config.initiator_contract, - self.rpc_provider.clone(), - ); - tracing::info!("Bridge transfer ID: {:?}", bridge_transfer_id); - let call = contract.refundBridgeTransfer(FixedBytes(bridge_transfer_id.0)); + let mapping_slot = U256::from(0); // the mapping is the zeroth slot in the contract + let key = bridge_transfer_id.0.clone(); + let storage_slot = calculate_storage_slot(key, mapping_slot); + let storage: U256 = self + .rpc_provider + .get_storage_at(self.native_contract_address(), storage_slot) + .await + .map_err(|_| generic_error("could not find storage"))?; + let storage_bytes = storage.to_be_bytes::<32>(); - send_transaction( - call, - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await - .map_err(|e| { - BridgeContractError::OnChainError(format!("Failed to send transaction: {}", e)) - })?; + println!("storage_bytes: {:?}", storage_bytes); + let mut storage_slice = &storage_bytes[..]; + let eth_details = EthBridgeTransferDetailsInitiate::decode(&mut storage_slice) + .map_err(|_| generic_error("could not decode storage"))?; - Ok(()) + Ok(Some(BridgeTransferInitiatedDetails { + bridge_transfer_id, + initiator: BridgeAddress(eth_details.originator), + recipient: BridgeAddress(eth_details.recipient.to_vec()), + amount: eth_details.amount.into(), + nonce: Nonce(eth_details.nonce.wrapping_to::()), + })) } +} - async fn lock_bridge_transfer( +#[async_trait::async_trait] +impl BridgeRelayerContract for EthClient { + async fn complete_bridge_transfer( &mut self, bridge_transfer_id: BridgeTransferId, - hash_lock: HashLock, initiator: BridgeAddress>, recipient: BridgeAddress, amount: Amount, + nonce: Nonce, ) -> BridgeContractResult<()> { - tracing::info!("Begin lockBridgeTransfer"); + let contract = NativeBridge::new(self.config.native_contract, self.rpc_provider.clone()); let initiator: [u8; 32] = initiator.0.try_into().map_err(|_| { - BridgeContractError::ConversionFailed("lock_bridge_transfer initiator".to_string()) + BridgeContractError::ConversionFailed("initiator must be exactly 32 bytes".to_string()) })?; - let call = self - .counterparty_contract - .lockBridgeTransfer( - FixedBytes(initiator), - FixedBytes(bridge_transfer_id.0), - FixedBytes(hash_lock.0), - *recipient.0, - U256::try_from(amount.0) - .map_err(|_| BridgeContractError::ConversionFailed("U256".to_string()))?, - ) - .from(self.signer_address); - - tracing::info!( - "Attempting lockBridgeTransfer with sender address: {:?}", - self.signer_address + let call = contract.completeBridgeTransfer( + FixedBytes(bridge_transfer_id.0), + FixedBytes(initiator), + recipient.0 .0, + U256::from(amount.0), + U256::from(nonce.0), ); - let receipt = send_transaction( - call, - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await - .map_err(|e| { - BridgeContractError::OnChainError(format!("Failed to send transaction: {}", e)) - })?; - - tracing::info!("LockBridgeTransfer receipt: {:?}", receipt); - - Ok(()) - } - - async fn abort_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()> { - let contract = AtomicBridgeCounterpartyMOVE::new( - self.config.counterparty_contract, - self.rpc_provider.clone(), - ); - let call = contract.abortBridgeTransfer(FixedBytes(bridge_transfer_id.0)); - send_transaction( - call, - self.signer_address, - &send_transaction_rules(), - self.config.transaction_send_retries, - self.config.gas_limit, - ) - .await - .map_err(|e| { - BridgeContractError::OnChainError(format!("Failed to send transaction: {}", e)) - })?; - let call = contract.abortBridgeTransfer(FixedBytes(bridge_transfer_id.0)); send_transaction( call, self.signer_address, @@ -399,68 +253,18 @@ impl bridge_util::chains::bridge_contracts::BridgeContract for EthCl Ok(()) } - async fn get_bridge_transfer_details_initiator( + async fn get_bridge_transfer_details_with_nonce( &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>> { - let generic_error = |desc| BridgeContractError::GenericError(String::from(desc)); - - let mapping_slot = U256::from(0); // the mapping is the zeroth slot in the contract - let key = bridge_transfer_id.0.clone(); - let storage_slot = calculate_storage_slot(key, mapping_slot); - let storage: U256 = self - .rpc_provider - .get_storage_at(self.initiator_contract_address(), storage_slot) - .await - .map_err(|_| generic_error("could not find storage"))?; - let storage_bytes = storage.to_be_bytes::<32>(); - - println!("storage_bytes: {:?}", storage_bytes); - let mut storage_slice = &storage_bytes[..]; - let eth_details = EthBridgeTransferDetails::decode(&mut storage_slice) - .map_err(|_| generic_error("could not decode storage"))?; - - Ok(Some(BridgeTransferDetails { - bridge_transfer_id, - initiator: BridgeAddress(eth_details.originator), - recipient: BridgeAddress(eth_details.recipient.to_vec()), - hash_lock: HashLock(eth_details.hash_lock), - time_lock: TimeLock(eth_details.time_lock.wrapping_to::()), - amount: eth_details.amount.into(), - state: eth_details.state, - })) + nonce: Nonce, + ) -> BridgeContractResult>> { + todo!() } - async fn get_bridge_transfer_details_counterparty( + async fn is_bridge_transfer_completed( &mut self, bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>> { - let generic_error = |desc| BridgeContractError::GenericError(String::from(desc)); - - let mapping_slot = U256::from(0); // the mapping is the zeroth slot in the contract - let key = bridge_transfer_id.0.clone(); - let storage_slot = calculate_storage_slot(key, mapping_slot); - let storage: U256 = self - .rpc_provider - .get_storage_at(self.initiator_contract_address(), storage_slot) - .await - .map_err(|_| generic_error("could not find storage"))?; - let storage_bytes = storage.to_be_bytes::<32>(); - - println!("storage_bytes: {:?}", storage_bytes); - let mut storage_slice = &storage_bytes[..]; - let eth_details = EthBridgeTransferDetailsCounterparty::decode(&mut storage_slice) - .map_err(|_| generic_error("could not decode storage"))?; - - Ok(Some(BridgeTransferDetailsCounterparty { - bridge_transfer_id, - initiator: BridgeAddress(eth_details.originator.to_vec()), - recipient: BridgeAddress(eth_details.recipient), - hash_lock: HashLock(eth_details.hash_lock), - time_lock: TimeLock(eth_details.time_lock.wrapping_to::()), - amount: eth_details.amount.into(), - state: eth_details.state, - })) + ) -> BridgeContractResult { + todo!() } } @@ -477,16 +281,14 @@ mod tests { #[test] fn test_wrapping_to_on_eth_details() { let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); - let eth_details = EthBridgeTransferDetails { + let eth_details = EthBridgeTransferDetailsInitiate { amount: U256::from(10u64.pow(18)), originator: EthAddress([0; 20].into()), recipient: [0; 32], - hash_lock: [0; 32], - time_lock: U256::from(current_time + 84600), // 1 day - state: 1, + nonce: U256::from(current_time + 84600), // 1 day }; test_wrapping_to(ð_details.amount, 10u64.pow(18)); - test_wrapping_to(ð_details.time_lock, current_time + 84600); + test_wrapping_to(ð_details.nonce, current_time + 84600); } #[test] @@ -495,16 +297,14 @@ mod tests { let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); let additional_time = rand::random::(); let random_amount = rand::random::(); - let eth_details = EthBridgeTransferDetails { + let eth_details = EthBridgeTransferDetailsInitiate { amount: U256::from(random_amount), originator: EthAddress([0; 20].into()), recipient: [0; 32], - hash_lock: [0; 32], - time_lock: U256::from(current_time + additional_time), - state: 1, + nonce: U256::from(current_time + additional_time), }; test_wrapping_to(ð_details.amount, random_amount); - test_wrapping_to(ð_details.time_lock, current_time + additional_time); + test_wrapping_to(ð_details.nonce, current_time + additional_time); } } } diff --git a/protocol-units/bridge/service/src/chains/ethereum/event_monitoring.rs b/protocol-units/bridge/service/src/chains/ethereum/event_monitoring.rs index d0b0555b4..04cdbd0db 100644 --- a/protocol-units/bridge/service/src/chains/ethereum/event_monitoring.rs +++ b/protocol-units/bridge/service/src/chains/ethereum/event_monitoring.rs @@ -1,6 +1,5 @@ use super::types::EthAddress; -use crate::chains::ethereum::types::AtomicBridgeCounterpartyMOVE; -use crate::chains::ethereum::types::AtomicBridgeInitiatorMOVE; +use crate::chains::ethereum::types::NativeBridge; use alloy::eips::BlockNumberOrTag; use alloy::primitives::Address; use alloy::providers::Provider; @@ -11,24 +10,89 @@ use bridge_util::chains::bridge_contracts::BridgeContractError; use bridge_util::chains::bridge_contracts::BridgeContractEvent; use bridge_util::chains::bridge_contracts::BridgeContractMonitoring; use bridge_util::chains::bridge_contracts::BridgeContractResult; -use bridge_util::types::HashLockPreImage; -use bridge_util::types::LockDetails; -use bridge_util::types::{BridgeAddress, BridgeTransferDetails, BridgeTransferId, HashLock}; +use bridge_util::chains::bridge_contracts::BridgeTransferCompletedDetails; +use bridge_util::chains::bridge_contracts::BridgeTransferInitiatedDetails; +use bridge_util::types::Nonce; +use bridge_util::types::{BridgeAddress, BridgeTransferId}; use futures::SinkExt; -use futures::{channel::mpsc::UnboundedReceiver, Stream, StreamExt}; +use futures::{ + channel::mpsc::{UnboundedReceiver, UnboundedSender}, + Stream, StreamExt, +}; +use std::sync::Arc; use std::{pin::Pin, task::Poll}; +use tokio::sync::RwLock; use tokio::sync::{mpsc, oneshot}; pub struct EthMonitoring { + pulling_task: Option, listener: UnboundedReceiver>>, } +impl EthMonitoring { + pub async fn build( + config: &EthConfig, + health_check_rx: mpsc::Receiver>, + ) -> Result { + let pulling_task = PullMonitoring::start_pulling(config, health_check_rx).await?; + let listener = pulling_task.add_notification_channel().await; + + Ok(Self { pulling_task: Some(pulling_task), listener }) + } + + pub async fn child(&self) -> Self { + let listener = self + .pulling_task + .as_ref() + .expect("EthMonitoring Clone on non initial object.") + .add_notification_channel() + .await; + Self { pulling_task: None, listener } + } +} impl BridgeContractMonitoring for EthMonitoring { type Address = EthAddress; } -impl EthMonitoring { - pub async fn build( +impl Stream for EthMonitoring { + type Item = BridgeContractResult>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { + let this = self.get_mut(); + this.listener.poll_next_unpin(cx) + } +} + +pub struct PullMonitoring { + notification_channel_list: + Arc>>>>>, +} + +impl PullMonitoring { + pub async fn add_notification_channel( + &self, + ) -> UnboundedReceiver>> { + let (sender, listener) = futures::channel::mpsc::unbounded::< + BridgeContractResult>, + >(); + let mut list = self.notification_channel_list.write().await; + list.push(sender); + listener + } + + async fn notify_event( + list: &Vec>>>, + event: BridgeContractResult>, + ) { + for ref mut notif in list { + if notif.send(event.clone()).await.is_err() { + tracing::error!("Eth Monitor Failed to send event to listener channel"); + break; + } + } + } + + pub async fn start_pulling( config: &EthConfig, mut health_check_rx: mpsc::Receiver>, ) -> Result { @@ -39,25 +103,16 @@ impl EthMonitoring { .on_builtin(client_config.rpc_url.as_str()) .await?; - tracing::info!( - "Start Eth monitoring with initiator:{} counterpart:{}", - config.eth_initiator_contract, - config.eth_counterparty_contract - ); + let notification_channel_list = Arc::new(RwLock::new(vec![])); - let (mut sender, listener) = futures::channel::mpsc::unbounded::< - BridgeContractResult>, - >(); + tracing::info!("Start Eth monitoring with initiator:{}", config.eth_native_contract,); tokio::spawn({ let config = config.clone(); + let notification_channel_list = notification_channel_list.clone(); async move { - let initiator_contract = AtomicBridgeInitiatorMOVE::new( - config.eth_initiator_contract.parse().unwrap(), //If unwrap start fail. Config must be updated. - rpc_provider.clone(), - ); - let counterpart_contract = AtomicBridgeCounterpartyMOVE::new( - config.eth_counterparty_contract.parse().unwrap(), //If unwrap start fail. Config must be updated. + let native_contract = NativeBridge::new( + config.eth_native_contract.parse().unwrap(), //If unwrap start fail. Config must be updated. rpc_provider.clone(), ); let mut last_processed_block = 0; @@ -86,62 +141,42 @@ impl EthMonitoring { { Ok(Ok(block_number)) => block_number, Ok(Err(err)) => { - if sender - .send(Err(BridgeContractError::OnChainError(format!( + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError(format!( "Eth get blocknumber request failed: {err}" - )))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + ))), + ) + .await; + let _ = tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; continue; } Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(format!( + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError(format!( "Eth get blocknumber timeout: {err}" - )))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + ))), + ) + .await; let _ = tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; continue; } }; if last_processed_block < block_number { last_processed_block = block_number; - let initiator_initiate_event_filter = initiator_contract + let initiate_event_filter = native_contract .BridgeTransferInitiated_filter() .from_block(BlockNumberOrTag::Number(last_processed_block)); // event BridgeTransferCompleted(bytes32 indexed _bridgeTransferId, bytes32 pre_image); - let initiator_trcompleted_event_filter = initiator_contract + let completed_event_filter = native_contract .BridgeTransferCompleted_filter() .from_block(BlockNumberOrTag::Number(last_processed_block)); - // event BridgeTransferRefunded(bytes32 indexed _bridgeTransferId); - let initiator_trrefund_event_filter = initiator_contract - .BridgeTransferRefunded_filter() - .from_block(BlockNumberOrTag::Number(last_processed_block)); - let counterpart_trlocked_event_filter = counterpart_contract - .BridgeTransferLocked_filter() - .from_block(BlockNumberOrTag::Number(last_processed_block)); - let counterpart_trcompleted_event_filter = counterpart_contract - .BridgeTransferCompleted_filter() - .from_block(BlockNumberOrTag::Number(last_processed_block)); - //event BridgeTransferAborted(bytes32 indexed bridgeTransferId); - let counterpart_trcaborted_event_filter = counterpart_contract - .BridgeTransferAborted_filter() - .from_block(BlockNumberOrTag::Number(last_processed_block)); - //Initiator event stream match tokio::time::timeout( tokio::time::Duration::from_secs(config.rest_connection_timeout_secs), - initiator_initiate_event_filter.query(), + initiate_event_filter.query(), ) .await { @@ -149,285 +184,99 @@ impl EthMonitoring { for (initiated, _log) in events { let event = { // BridgeTransferInitiated(bridgeTransferId, originator, recipient, totalAmount, hashLock, initiatorTimeLockDuration); - let details: BridgeTransferDetails = - BridgeTransferDetails { + let details: BridgeTransferInitiatedDetails = + BridgeTransferInitiatedDetails { bridge_transfer_id: BridgeTransferId( - *initiated._bridgeTransferId, + *initiated.bridgeTransferId, ), initiator: BridgeAddress(EthAddress( - Address::from(initiated._originator), + Address::from(initiated.originator), )), recipient: BridgeAddress( - initiated._recipient.to_vec(), + initiated.recipient.to_vec(), ), - hash_lock: HashLock(*initiated._hashLock), - time_lock: initiated._timeLock.into(), + nonce: Nonce(initiated.nonce.wrapping_to::()), amount: initiated.amount.into(), - state: 0, }; BridgeContractEvent::Initiated(details) }; - if sender.send(Ok(event)).await.is_err() { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query initiator_initiate_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - match tokio::time::timeout( - tokio::time::Duration::from_secs(config.rest_connection_timeout_secs),initiator_trcompleted_event_filter.query()).await { - Ok(Ok(events)) => { - for (completed, _log) in events { - if sender - .send(Ok(BridgeContractEvent::InitiatorCompleted( - BridgeTransferId(*completed._bridgeTransferId), - ))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Ok(event), + ) + .await; } } Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query initiator_trcompleted_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError("Eth monitoring query initiator_initiate_event_filter timeout.".to_string())), + ).await; } Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError(err.to_string())), + ) + .await; } } match tokio::time::timeout( tokio::time::Duration::from_secs(config.rest_connection_timeout_secs), - initiator_trrefund_event_filter.query(), - ) - .await - { - Ok(Ok(events)) => { - for (refund, _log) in events { - if sender - .send(Ok(BridgeContractEvent::Refunded(BridgeTransferId( - *refund._bridgeTransferId, - )))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query initiator_trrefund_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - tracing::info!("Querying counterpart_trlocked_event_filter"); - let start_time = tokio::time::Instant::now(); - match tokio::time::timeout( - tokio::time::Duration::from_secs(30), - counterpart_trlocked_event_filter.query(), + completed_event_filter.query(), ) .await { Ok(Ok(events)) => { - tracing::info!( - "Query completed for counterpart_trlocked_event_filter in {:?}", - start_time.elapsed() - ); - for (trlocked, _log) in events { + for (completed, _log) in events { let event = { - // BridgeTransferLocked(bridgeTransferId, originator, recipient, totalAmount, hashLock, initiatorTimeLockDuration); - let details: LockDetails = LockDetails { - bridge_transfer_id: BridgeTransferId( - *trlocked.bridgeTransferId, - ), - initiator: BridgeAddress([0, 32].into()), // TODO add the originator fields. trlocked.originator.to_vec() - recipient: BridgeAddress(EthAddress(Address::from( - trlocked.recipient, - ))), - amount: trlocked.amount.into(), - hash_lock: HashLock(*trlocked.hashLock), - time_lock: trlocked.timeLock.into(), - }; - BridgeContractEvent::Locked(details) + // BridgeTransferInitiated(bridgeTransferId, originator, recipient, totalAmount, hashLock, initiatorTimeLockDuration); + let details: BridgeTransferCompletedDetails = + BridgeTransferCompletedDetails { + bridge_transfer_id: BridgeTransferId( + *completed.bridgeTransferId, + ), + initiator: BridgeAddress( + completed.originator.to_vec(), + ), + recipient: BridgeAddress(EthAddress( + Address::from(completed.recipient), + )), + nonce: bridge_util::types::Nonce( + completed.nonce.wrapping_to::(), + ), + amount: completed.amount.into(), + }; + BridgeContractEvent::Completed(details) }; - if sender.send(Ok(event)).await.is_err() { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query counterpart_trlocked_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - match tokio::time::timeout( - tokio::time::Duration::from_secs(config.rest_connection_timeout_secs), - counterpart_trcompleted_event_filter.query(), - ) - .await - { - Ok(Ok(events)) => { - for (completed, _log) in events { - if sender - .send(Ok(BridgeContractEvent::CounterPartyCompleted( - BridgeTransferId(*completed.bridgeTransferId), - HashLockPreImage(*completed.pre_image), - ))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query counterpart_trcompleted_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } - } - } - match tokio::time::timeout( - tokio::time::Duration::from_secs(config.rest_connection_timeout_secs), - counterpart_trcaborted_event_filter.query(), - ) - .await - { - Ok(Ok(events)) => { - for (aborted, _log) in events { - if sender - .send(Ok(BridgeContractEvent::Cancelled(BridgeTransferId( - *aborted.bridgeTransferId, - )))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Ok(event), + ) + .await; } } Ok(Err(_)) => { - if sender - .send(Err(BridgeContractError::OnChainError("Eth monitoring query counterpart_trcaborted_event_filter timeout.".to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError("Eth monitoring query initiator_trcompleted_event_filter timeout.".to_string())), + ).await; } Err(err) => { - if sender - .send(Err(BridgeContractError::OnChainError(err.to_string()))) - .await - .is_err() - { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + Err(BridgeContractError::OnChainError(err.to_string())), + ) + .await; } } - } // end match + } // end if let _ = tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; } // end loop } // End spawn }); - Ok(Self { listener }) - } -} - -impl Stream for EthMonitoring { - type Item = BridgeContractResult>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { - let this = self.get_mut(); - this.listener.poll_next_unpin(cx) + Ok(PullMonitoring { notification_channel_list }) } } diff --git a/protocol-units/bridge/service/src/chains/ethereum/types.rs b/protocol-units/bridge/service/src/chains/ethereum/types.rs index ed1da9961..2e371e77b 100644 --- a/protocol-units/bridge/service/src/chains/ethereum/types.rs +++ b/protocol-units/bridge/service/src/chains/ethereum/types.rs @@ -7,13 +7,7 @@ use alloy::providers::fillers::{ use alloy::providers::RootProvider; use alloy::rlp::{RlpDecodable, RlpEncodable}; use alloy::transports::BoxTransport; -use bridge_util::types::Amount; use bridge_util::types::BridgeAddress; -use bridge_util::types::BridgeTransferDetails; -use bridge_util::types::BridgeTransferId; -use bridge_util::types::HashLock; -use bridge_util::types::HashLockPreImage; -use bridge_util::types::LockDetails; use rand::Rng; use std::hash::{DefaultHasher, Hash, Hasher}; @@ -23,14 +17,8 @@ pub const ETH_ADDRESS_LEN: usize = 20; alloy::sol!( #[allow(missing_docs)] #[sol(rpc)] - AtomicBridgeInitiatorMOVE, - "abis/AtomicBridgeInitiatorMOVE.json" -); -alloy::sol!( - #[allow(missing_docs)] - #[sol(rpc)] - AtomicBridgeCounterpartyMOVE, - "abis/AtomicBridgeCounterpartyMOVE.json" + NativeBridge, + "abis/NativeBridge.json" ); alloy::sol!( @@ -73,16 +61,6 @@ impl EthHash { } } -impl From for EthHash { - fn from(value: HashLock) -> Self { - let mut fixed_bytes = [0u8; 32]; - let len = value.0.len().min(32); - fixed_bytes[..len].copy_from_slice(&value.0[..len]); - - Self(hash_vec_u32(&fixed_bytes)) - } -} - pub fn hash_vec_u32(data: &[u8; 32]) -> [u8; 32] { let mut result = [0u8; 32]; @@ -107,10 +85,7 @@ pub fn hash_static_string(pre_image: &'static str) -> [u8; 32] { hash_vec_u32(&fixed_bytes) } -pub type InitiatorContract = - AtomicBridgeInitiatorMOVE::AtomicBridgeInitiatorMOVEInstance; -pub type CounterpartyContract = - AtomicBridgeCounterpartyMOVE::AtomicBridgeCounterpartyMOVEInstance; +pub type NativeBridgeContract = NativeBridge::NativeBridgeInstance; pub type AlloyProvider = FillProvider< JoinFill< @@ -174,40 +149,3 @@ impl From<[u8; 20]> for EthAddress { EthAddress(Address(bytes.into())) } } - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct CompletedDetails { - pub bridge_transfer_id: BridgeTransferId, - pub recipient: BridgeAddress, - pub hash_lock: HashLock, - pub secret: HashLockPreImage, - pub amount: Amount, -} - -impl CompletedDetails -where - A: From>, -{ - pub fn from_bridge_transfer_details( - bridge_transfer_details: BridgeTransferDetails>, - secret: HashLockPreImage, - ) -> Self { - CompletedDetails { - bridge_transfer_id: bridge_transfer_details.bridge_transfer_id, - recipient: BridgeAddress(A::from(bridge_transfer_details.recipient.0)), - hash_lock: bridge_transfer_details.hash_lock, - secret, - amount: bridge_transfer_details.amount, - } - } - - pub fn from_lock_details(lock_details: LockDetails, secret: HashLockPreImage) -> Self { - CompletedDetails { - bridge_transfer_id: lock_details.bridge_transfer_id, - recipient: lock_details.recipient, - hash_lock: lock_details.hash_lock, - secret, - amount: lock_details.amount, - } - } -} diff --git a/protocol-units/bridge/service/src/chains/mod.rs b/protocol-units/bridge/service/src/chains/mod.rs index 6bd79ec92..32db03382 100644 --- a/protocol-units/bridge/service/src/chains/mod.rs +++ b/protocol-units/bridge/service/src/chains/mod.rs @@ -1,3 +1,2 @@ pub mod ethereum; pub mod movement; -pub use bridge_util::chains::*; diff --git a/protocol-units/bridge/service/src/chains/movement/client_framework.rs b/protocol-units/bridge/service/src/chains/movement/client_framework.rs index 4fb58c3e6..9e7259cae 100644 --- a/protocol-units/bridge/service/src/chains/movement/client_framework.rs +++ b/protocol-units/bridge/service/src/chains/movement/client_framework.rs @@ -8,16 +8,16 @@ use aptos_sdk::{ }; use aptos_types::account_address::AccountAddress; use bridge_config::common::movement::MovementConfig; +use bridge_util::chains::bridge_contracts::BridgeTransferInitiatedDetails; +use bridge_util::types::Nonce; use bridge_util::{ - chains::bridge_contracts::{BridgeContract, BridgeContractError, BridgeContractResult}, - types::{ - Amount, BridgeAddress, BridgeTransferDetails, BridgeTransferDetailsCounterparty, - BridgeTransferId, HashLock, HashLockPreImage, TimeLock, + chains::bridge_contracts::{ + BridgeClientContract, BridgeContractError, BridgeContractResult, BridgeRelayerContract, }, + types::{Amount, BridgeAddress, BridgeTransferId}, }; use hex; -use rand::prelude::*; -use std::{path::Path, str::FromStr, sync::Arc}; +use std::{str::FromStr, sync::Arc}; use tracing::{debug, info}; use url::Url; @@ -25,9 +25,7 @@ pub const FRAMEWORK_ADDRESS: AccountAddress = AccountAddress::new([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]); -pub const INITIATOR_MODULE_NAME: &str = "atomic_bridge_initiator"; -pub const COUNTERPARTY_MODULE_NAME: &str = "atomic_bridge_counterparty"; -const DUMMY_ADDRESS: AccountAddress = AccountAddress::new([0; 32]); +pub const NATIVE_BRIDGE_MODULE_NAME: &str = "native_bridge"; #[allow(dead_code)] enum Call { @@ -49,7 +47,7 @@ pub struct MovementClientFramework { } impl MovementClientFramework { - pub async fn new(config: &MovementConfig) -> Result { + pub async fn build_with_config(config: &MovementConfig) -> Result { let node_connection_url = Url::from_str(config.mvt_rpc_connection_url().as_str()) .map_err(|_| BridgeContractError::SerializationError)?; @@ -61,6 +59,18 @@ impl MovementClientFramework { Ok(MovementClientFramework { native_address, rest_client, signer: Arc::new(signer) }) } + pub async fn build_with_signer( + signer: LocalAccount, + config: &MovementConfig, + ) -> Result { + let node_connection_url = Url::from_str(config.mvt_rpc_connection_url().as_str()) + .map_err(|_| BridgeContractError::SerializationError)?; + + let rest_client = Client::new(node_connection_url.clone()); + let native_address = AccountAddress::from_hex_literal(&config.movement_native_address)?; + Ok(MovementClientFramework { native_address, rest_client, signer: Arc::new(signer) }) + } + pub fn rest_client(&self) -> &Client { &self.rest_client } @@ -68,72 +78,25 @@ impl MovementClientFramework { pub fn signer(&self) -> &LocalAccount { &self.signer } - - pub async fn initiator_set_timelock( - &mut self, - time_lock: u64, - ) -> Result<(), BridgeContractError> { - let args = vec![utils::serialize_u64(&time_lock)?]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - "atomic_bridge_configuration", - "set_initiator_time_lock_duration", - Vec::new(), - args, - ); - - utils::send_and_confirm_aptos_transaction(&self.rest_client, self.signer.as_ref(), payload) - .await - .map_err(|_| BridgeContractError::CallError)?; - - Ok(()) - } - - pub async fn counterparty_set_timelock( - &mut self, - time_lock: u64, - ) -> Result<(), BridgeContractError> { - let args = vec![utils::serialize_u64(&time_lock)?]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - "atomic_bridge_configuration", - "set_counterparty_time_lock_duration", - Vec::new(), - args, - ); - - utils::send_and_confirm_aptos_transaction(&self.rest_client, self.signer.as_ref(), payload) - .await - .map_err(|_| BridgeContractError::CallError)?; - - Ok(()) - } } #[async_trait::async_trait] -impl BridgeContract for MovementClientFramework { +impl BridgeClientContract for MovementClientFramework { async fn initiate_bridge_transfer( &mut self, - _initiator: BridgeAddress, recipient: BridgeAddress>, - hash_lock: HashLock, amount: Amount, ) -> BridgeContractResult<()> { - debug!("Amount value: {:?}", amount); - - let serialized_hash_lock = utils::serialize_vec_initiator(&hash_lock.0[..])?; + tracing::info!("Amount value: {:?}", amount); let args = vec![ utils::serialize_vec_initiator(&recipient.0)?, - serialized_hash_lock, - utils::serialize_u64_initiator(&amount)?, + utils::serialize_u64_initiator(*amount)?, ]; let payload = utils::make_aptos_payload( FRAMEWORK_ADDRESS, - "atomic_bridge_initiator", + NATIVE_BRIDGE_MODULE_NAME, "initiate_bridge_transfer", Vec::new(), args, @@ -149,170 +112,11 @@ impl BridgeContract for MovementClientFramework { Ok(()) } - - async fn initiator_complete_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - preimage: HashLockPreImage, - ) -> BridgeContractResult<()> { - let unpadded_preimage = { - let mut end = preimage.0.len(); - while end > 0 && preimage.0[end - 1] == 0 { - end -= 1; - } - &preimage.0[..end] - }; - println!("Unpadded preimage: {:?}", unpadded_preimage); - let args2 = vec![ - utils::serialize_vec_initiator(&bridge_transfer_id.0[..])?, - utils::serialize_vec_initiator(unpadded_preimage)?, - ]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - INITIATOR_MODULE_NAME, - "complete_bridge_transfer", - Vec::new(), - args2, - ); - - let _ = utils::send_and_confirm_aptos_transaction( - &self.rest_client, - self.signer.as_ref(), - payload, - ) - .await - .map_err(|_| BridgeContractError::CompleteTransferError); - - Ok(()) - } - - async fn counterparty_complete_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - preimage: HashLockPreImage, - ) -> BridgeContractResult<()> { - let unpadded_preimage = { - let mut end = preimage.0.len(); - while end > 0 && preimage.0[end - 1] == 0 { - end -= 1; - } - &preimage.0[..end] - }; - let args2 = vec![ - utils::serialize_vec(&bridge_transfer_id.0[..])?, - utils::serialize_vec(&unpadded_preimage)?, - ]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - COUNTERPARTY_MODULE_NAME, - "complete_bridge_transfer", - Vec::new(), - args2, - ); - - let result = utils::send_and_confirm_aptos_transaction( - &self.rest_client, - self.signer.as_ref(), - payload, - ) - .await - .map_err(|_| BridgeContractError::CompleteTransferError); - - match &result { - Ok(tx_result) => { - debug!("Transaction succeeded: {:?}", tx_result); - } - Err(err) => { - debug!("Transaction failed: {:?}", err); - } - } - - Ok(()) - } - - async fn lock_bridge_transfer( + + async fn get_bridge_transfer_details( &mut self, bridge_transfer_id: BridgeTransferId, - hash_lock: HashLock, - initiator: BridgeAddress>, - recipient: BridgeAddress, - amount: Amount, - ) -> BridgeContractResult<()> { - debug!("Starting lock bridge transfer"); - debug!("Initiator: {:?}", initiator.0); - - let args = vec![ - utils::serialize_vec(&initiator.0)?, - utils::serialize_vec(&bridge_transfer_id.0[..])?, - utils::serialize_vec(&hash_lock.0[..])?, - utils::serialize_vec(&recipient.0)?, - utils::serialize_u64(&amount)?, - ]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - COUNTERPARTY_MODULE_NAME, - "lock_bridge_transfer_assets", - Vec::new(), - args, - ); - - let _ = utils::send_and_confirm_aptos_transaction( - &self.rest_client, - self.signer.as_ref(), - payload, - ) - .await - .map_err(|_| BridgeContractError::LockTransferError)?; - - Ok(()) - } - - async fn refund_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()> { - let args = vec![utils::serialize_vec_initiator(&bridge_transfer_id.0[..])?]; - - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - "atomic_bridge_initiator", - "refund_bridge_transfer", - Vec::new(), - args, - ); - - utils::send_and_confirm_aptos_transaction(&self.rest_client, self.signer.as_ref(), payload) - .await - .map_err(|err| BridgeContractError::OnChainError(err.to_string()))?; - - Ok(()) - } - - async fn abort_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()> { - let args3 = vec![utils::serialize_vec(&bridge_transfer_id.0[..])?]; - let payload = utils::make_aptos_payload( - FRAMEWORK_ADDRESS, - COUNTERPARTY_MODULE_NAME, - "abort_bridge_transfer", - Vec::new(), - args3, - ); - utils::send_and_confirm_aptos_transaction(&self.rest_client, self.signer.as_ref(), payload) - .await - .map_err(|_| BridgeContractError::AbortTransferError)?; - Ok(()) - } - - async fn get_bridge_transfer_details_initiator( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>> { + ) -> BridgeContractResult>> { let bridge_transfer_id_hex = format!("0x{}", hex::encode(bridge_transfer_id.0)); let view_request = ViewRequest { @@ -367,590 +171,171 @@ impl BridgeContract for MovementClientFramework { .parse::() .map_err(|_| BridgeContractError::SerializationError)?; - let hash_lock_array: [u8; 32] = hex::decode( - &value["hash_lock"].as_str().ok_or(BridgeContractError::SerializationError)?[2..], - ) - .map_err(|_| BridgeContractError::SerializationError)? - .try_into() - .map_err(|_| BridgeContractError::SerializationError)?; - - let time_lock = value["time_lock"] + let time_lock = value["nonce"] .as_str() .ok_or(BridgeContractError::SerializationError)? - .parse::() + .parse::() .map_err(|_| BridgeContractError::SerializationError)?; let state = value["state"].as_u64().ok_or(BridgeContractError::SerializationError)? as u8; - let details = BridgeTransferDetails { + let details = BridgeTransferInitiatedDetails { bridge_transfer_id, initiator: BridgeAddress(MovementAddress(originator_address)), recipient: BridgeAddress(recipient_bytes), amount: Amount(amount), - hash_lock: HashLock(hash_lock_array), - time_lock: TimeLock(time_lock), - state, + nonce: Nonce(time_lock), }; Ok(Some(details)) } - async fn get_bridge_transfer_details_counterparty( + // async fn get_bridge_transfer_details_counterparty( + // &mut self, + // bridge_transfer_id: BridgeTransferId, + // ) -> BridgeContractResult>> { + // let bridge_transfer_id_hex = format!("0x{}", hex::encode(bridge_transfer_id.0)); + + // let view_request = ViewRequest { + // function: EntryFunctionId { + // module: MoveModuleId { + // address: FRAMEWORK_ADDRESS.clone().into(), + // name: aptos_api_types::IdentifierWrapper( + // Identifier::new("atomic_bridge_store") + // .map_err(|_| BridgeContractError::FunctionViewError)?, + // ), + // }, + // name: aptos_api_types::IdentifierWrapper( + // Identifier::new("get_bridge_transfer_details_counterparty") + // .map_err(|_| BridgeContractError::FunctionViewError)?, + // ), + // }, + // type_arguments: vec![], + // arguments: vec![serde_json::json!(bridge_transfer_id_hex)], + // }; + + // let response: Response> = self + // .rest_client + // .view(&view_request, None) + // .await + // .map_err(|_| BridgeContractError::CallError)?; + + // let values = response.inner(); + + // if values.len() != 1 { + // return Err(BridgeContractError::InvalidResponseLength); + // } + + // let value = &values[0]; + + // let originator_address_bytes = hex::decode( + // &value["addresses"]["initiator"]["inner"] + // .as_str() + // .ok_or(BridgeContractError::SerializationError)?[2..], + // ) + // .map_err(|_| BridgeContractError::SerializationError)?; + + // let recipient = AccountAddress::from_hex_literal( + // value["addresses"]["recipient"] + // .as_str() + // .ok_or(BridgeContractError::SerializationError)?, + // ) + // .map_err(|_| BridgeContractError::SerializationError)?; + + // let amount = value["amount"] + // .as_str() + // .ok_or(BridgeContractError::SerializationError)? + // .parse::() + // .map_err(|_| BridgeContractError::SerializationError)?; + + // let hash_lock_array: [u8; 32] = hex::decode( + // &value["hash_lock"].as_str().ok_or(BridgeContractError::SerializationError)?[2..], + // ) + // .map_err(|_| BridgeContractError::SerializationError)? + // .try_into() + // .map_err(|_| BridgeContractError::SerializationError)?; + + // let time_lock = value["time_lock"] + // .as_str() + // .ok_or(BridgeContractError::SerializationError)? + // .parse::() + // .map_err(|_| BridgeContractError::SerializationError)?; + + // let state = value["state"].as_u64().ok_or(BridgeContractError::SerializationError)? as u8; + + // let details = BridgeTransferDetailsCounterparty { + // bridge_transfer_id, + // initiator: BridgeAddress(originator_address_bytes), + // recipient: BridgeAddress(MovementAddress(recipient)), + // amount: Amount(amount), + // hash_lock: HashLock(hash_lock_array), + // time_lock: TimeLock(time_lock), + // state, + // }; + + // Ok(Some(details)) + // } +} + +#[async_trait::async_trait] +impl BridgeRelayerContract for MovementClientFramework { + async fn complete_bridge_transfer( &mut self, bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>> { - let bridge_transfer_id_hex = format!("0x{}", hex::encode(bridge_transfer_id.0)); - - let view_request = ViewRequest { - function: EntryFunctionId { - module: MoveModuleId { - address: FRAMEWORK_ADDRESS.clone().into(), - name: aptos_api_types::IdentifierWrapper( - Identifier::new("atomic_bridge_store") - .map_err(|_| BridgeContractError::FunctionViewError)?, - ), - }, - name: aptos_api_types::IdentifierWrapper( - Identifier::new("get_bridge_transfer_details_counterparty") - .map_err(|_| BridgeContractError::FunctionViewError)?, - ), - }, - type_arguments: vec![], - arguments: vec![serde_json::json!(bridge_transfer_id_hex)], - }; - - let response: Response> = self - .rest_client - .view(&view_request, None) - .await - .map_err(|_| BridgeContractError::CallError)?; - - let values = response.inner(); - - if values.len() != 1 { - return Err(BridgeContractError::InvalidResponseLength); - } - - let value = &values[0]; - - let originator_address_bytes = hex::decode( - &value["addresses"]["initiator"]["inner"] - .as_str() - .ok_or(BridgeContractError::SerializationError)?[2..], - ) - .map_err(|_| BridgeContractError::SerializationError)?; + initiator: BridgeAddress>, + recipient: BridgeAddress, + amount: Amount, + nonce: Nonce, + ) -> BridgeContractResult<()> { + let args = vec![ + utils::serialize_vec(&bridge_transfer_id.0[..])?, + utils::serialize_vec_initiator(&initiator.0)?, + utils::serialize_vec_initiator(&recipient.0)?, + utils::serialize_u64_initiator(*amount)?, + utils::serialize_u64_initiator(nonce.0.try_into().unwrap())?, + ]; - let recipient = AccountAddress::from_hex_literal( - value["addresses"]["recipient"] - .as_str() - .ok_or(BridgeContractError::SerializationError)?, - ) - .map_err(|_| BridgeContractError::SerializationError)?; + info!("The complete_bridge_transfer args are: {:?}", args); - let amount = value["amount"] - .as_str() - .ok_or(BridgeContractError::SerializationError)? - .parse::() - .map_err(|_| BridgeContractError::SerializationError)?; + let payload = utils::make_aptos_payload( + FRAMEWORK_ADDRESS, + NATIVE_BRIDGE_MODULE_NAME, + "complete_bridge_transfer", + Vec::new(), + args, + ); - let hash_lock_array: [u8; 32] = hex::decode( - &value["hash_lock"].as_str().ok_or(BridgeContractError::SerializationError)?[2..], + let result = utils::send_and_confirm_aptos_transaction( + &self.rest_client, + self.signer.as_ref(), + payload, ) - .map_err(|_| BridgeContractError::SerializationError)? - .try_into() - .map_err(|_| BridgeContractError::SerializationError)?; - - let time_lock = value["time_lock"] - .as_str() - .ok_or(BridgeContractError::SerializationError)? - .parse::() - .map_err(|_| BridgeContractError::SerializationError)?; - - let state = value["state"].as_u64().ok_or(BridgeContractError::SerializationError)? as u8; - - let details = BridgeTransferDetailsCounterparty { - bridge_transfer_id, - initiator: BridgeAddress(originator_address_bytes), - recipient: BridgeAddress(MovementAddress(recipient)), - amount: Amount(amount), - hash_lock: HashLock(hash_lock_array), - time_lock: TimeLock(time_lock), - state, - }; - - Ok(Some(details)) - } -} - -//@TODO: feature flag from here for testing only - -use std::{ - env, fs, - io::Write, - path::PathBuf, - process::{Command, Stdio}, -}; -use tokio::{ - io::{AsyncBufReadExt, BufReader}, - process::Command as TokioCommand, - sync::oneshot, - task, -}; - -impl MovementClientFramework { - pub async fn bridge_setup_scripts() -> Result<()> { - let current_dir = env::current_dir().expect("Failed to get current directory"); - println!("Current directory: {:?}", current_dir); - let project_root = Path::new("../../../"); - env::set_current_dir(&project_root) - .context("Failed to change directory to project root")?; - - let compile_output = Command::new("movement") - .args(&["move", "compile", "--package-dir", "protocol-units/bridge/move-modules/"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !compile_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&compile_output.stdout)); - } - if !compile_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&compile_output.stderr)); - } - - let enable_bridge_feature_output = Command::new("movement") - .args(&[ - "move", - "run-script", - "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/enable_bridge_feature.mv", - "--profile", - "default", - "--assume-yes", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !enable_bridge_feature_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&enable_bridge_feature_output.stdout)); - } - if !enable_bridge_feature_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&enable_bridge_feature_output.stderr)); - } - - let store_mint_burn_caps_output = Command::new("movement") - .args(&[ - "move", - "run-script", - "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/store_mint_burn_caps.mv", - "--profile", - "default", - "--assume-yes", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !store_mint_burn_caps_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&store_mint_burn_caps_output.stdout)); - } - if !store_mint_burn_caps_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&store_mint_burn_caps_output.stderr)); - } - - let update_bridge_operator_output = Command::new("movement") - .args(&[ - "move", - "run-script", - "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/update_bridge_operator.mv", - "--args", - "address:0xf90391c81027f03cdea491ed8b36ffaced26b6df208a9b569e5baf2590eb9b16", - "--profile", - "default", - "--assume-yes", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !update_bridge_operator_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&update_bridge_operator_output.stdout)); - } - if !update_bridge_operator_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&update_bridge_operator_output.stderr)); - } - - let set_initiator_time_lock_script_output = Command::new("movement") - .args(&[ - "move", - "run-script", - "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/set_initiator_time_lock_duration.mv", - "--args", - "u64: 11", - "--profile", - "default", - "--assume-yes", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !set_initiator_time_lock_script_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&update_bridge_operator_output.stdout)); - } - if !set_initiator_time_lock_script_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&update_bridge_operator_output.stderr)); - } + .await + .map_err(|_| BridgeContractError::CompleteTransferError); - let set_counterparty_time_lock_script_output = Command::new("movement") - .args(&[ - "move", - "run-script", - "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/set_counterparty_time_lock_duration.mv", - "--args", - "u64: 5", - "--profile", - "default", - "--assume-yes", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; - - if !set_counterparty_time_lock_script_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&update_bridge_operator_output.stdout)); - } - if !set_counterparty_time_lock_script_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&update_bridge_operator_output.stderr)); + match &result { + Ok(tx_result) => { + debug!("Transaction succeeded: {:?}", tx_result); + } + Err(err) => { + debug!("Transaction failed: {:?}", err); + } } Ok(()) } - pub fn publish_for_test(&mut self) -> Result<()> { - let random_seed = rand::thread_rng().gen_range(0, 1000000).to_string(); - - let mut process = Command::new("movement") - .args(&["init"]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to execute command"); - - let private_key_hex = hex::encode(self.signer.private_key().to_bytes()); - - let stdin: &mut std::process::ChildStdin = - process.stdin.as_mut().expect("Failed to open stdin"); - - let movement_dir = PathBuf::from(".movement"); - - if movement_dir.exists() { - stdin.write_all(b"yes\n").expect("Failed to write to stdin"); - } - - stdin.write_all(b"local\n").expect("Failed to write to stdin"); - - let _ = stdin.write_all(format!("{}\n", private_key_hex).as_bytes()); - - let addr_output = process.wait_with_output().expect("Failed to read command output"); - - if !addr_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&addr_output.stdout)); - } - - if !addr_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&addr_output.stderr)); - } - let addr_output_str = String::from_utf8_lossy(&addr_output.stderr); - let address = addr_output_str - .split_whitespace() - .find(|word| word.starts_with("0x")) - .expect("Failed to extract the Movement account address"); - - println!("Extracted address: {}", address); - - let resource_output = Command::new("movement") - .args(&[ - "account", - "derive-resource-account-address", - "--address", - address, - "--seed", - &random_seed, - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .expect("Failed to execute command"); - - // Print the output of the resource address command for debugging - if !resource_output.stdout.is_empty() { - println!("stdout: {}", String::from_utf8_lossy(&resource_output.stdout)); - } - if !resource_output.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&resource_output.stderr)); - } - - // Extract the resource address from the JSON output - let resource_output_str = String::from_utf8_lossy(&resource_output.stdout); - let resource_address = resource_output_str - .lines() - .find(|line| line.contains("\"Result\"")) - .and_then(|line| line.split('"').nth(3)) - .expect("Failed to extract the resource account address"); - - // Ensure the address has a "0x" prefix - let formatted_resource_address = if resource_address.starts_with("0x") { - resource_address.to_string() - } else { - format!("0x{}", resource_address) - }; - - // Set counterparty module address to resource address, for function calls: - self.native_address = AccountAddress::from_hex_literal(&formatted_resource_address)?; - - println!("Derived resource address: {}", formatted_resource_address); - - let current_dir = env::current_dir().expect("Failed to get current directory"); - println!("Current directory: {:?}", current_dir); - - let move_toml_path = PathBuf::from("../move-modules/Move.toml"); - - // Read the existing content of Move.toml - let move_toml_content = - fs::read_to_string(&move_toml_path).expect("Failed to read Move.toml file"); - - // Update the content of Move.toml with the new addresses - let updated_content = move_toml_content - .lines() - .map(|line| match line { - _ if line.starts_with("resource_addr = ") => { - format!(r#"resource_addr = "{}""#, formatted_resource_address) - } - _ if line.starts_with("atomic_bridge = ") => { - format!(r#"atomic_bridge = "{}""#, formatted_resource_address) - } - _ if line.starts_with("moveth = ") => { - format!(r#"moveth = "{}""#, formatted_resource_address) - } - _ if line.starts_with("master_minter = ") => { - format!(r#"master_minter = "{}""#, formatted_resource_address) - } - _ if line.starts_with("minter = ") => { - format!(r#"minter = "{}""#, formatted_resource_address) - } - _ if line.starts_with("admin = ") => { - format!(r#"admin = "{}""#, formatted_resource_address) - } - _ if line.starts_with("origin_addr = ") => { - format!(r#"origin_addr = "{}""#, address) - } - _ if line.starts_with("source_account = ") => { - format!(r#"source_account = "{}""#, address) - } - _ => line.to_string(), - }) - .collect::>() - .join("\n"); - - // Write the updated content back to Move.toml - let mut file = - fs::File::create(&move_toml_path).expect("Failed to open Move.toml file for writing"); - file.write_all(updated_content.as_bytes()) - .expect("Failed to write updated Move.toml file"); - - println!("Move.toml updated successfully."); - - let output2 = Command::new("movement") - .args(&[ - "move", - "create-resource-account-and-publish-package", - "--assume-yes", - "--address-name", - "moveth", - "--seed", - &random_seed, - "--package-dir", - "../move-modules", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .expect("Failed to execute command"); - - if !output2.stdout.is_empty() { - eprintln!("stdout: {}", String::from_utf8_lossy(&output2.stdout)); - } - - if !output2.stderr.is_empty() { - eprintln!("stderr: {}", String::from_utf8_lossy(&output2.stderr)); - } - - if movement_dir.exists() { - fs::remove_dir_all(movement_dir).expect("Failed to delete .movement directory"); - println!(".movement directory deleted successfully."); - } - - // Read the existing content of Move.toml - let move_toml_content = - fs::read_to_string(&move_toml_path).expect("Failed to read Move.toml file"); - - // Directly assign the address - let final_address = "0xcafe"; - - // Directly assign the formatted resource address - let final_formatted_resource_address = - "0xc3bb8488ab1a5815a9d543d7e41b0e0df46a7396f89b22821f07a4362f75ddc5"; - - let updated_content = move_toml_content - .lines() - .map(|line| match line { - _ if line.starts_with("resource_addr = ") => { - format!(r#"resource_addr = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("atomic_bridge = ") => { - format!(r#"atomic_bridge = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("moveth = ") => { - format!(r#"moveth = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("master_minter = ") => { - format!(r#"master_minter = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("minter = ") => { - format!(r#"minter = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("admin = ") => { - format!(r#"admin = "{}""#, final_formatted_resource_address) - } - _ if line.starts_with("origin_addr = ") => { - format!(r#"origin_addr = "{}""#, final_address) - } - _ if line.starts_with("pauser = ") => { - format!(r#"pauser = "{}""#, "0xdafe") - } - _ if line.starts_with("denylister = ") => { - format!(r#"denylister = "{}""#, "0xcade") - } - _ => line.to_string(), - }) - .collect::>() - .join("\n"); - - // Write the updated content back to Move.toml - let mut file = - fs::File::create(&move_toml_path).expect("Failed to open Move.toml file for writing"); - file.write_all(updated_content.as_bytes()) - .expect("Failed to write updated Move.toml file"); - - println!("Move.toml addresses updated successfully at the end of the test."); - - Ok(()) + async fn get_bridge_transfer_details_with_nonce( + &mut self, + nonce: Nonce, + ) -> BridgeContractResult>> { + todo!() } - pub async fn new_for_test() -> Result<(Self, tokio::process::Child), anyhow::Error> { - let kill_cmd = TokioCommand::new("sh") - .arg("-c") - .arg("PID=$(ps aux | grep 'movement node run-local-testnet' | grep -v grep | awk '{print $2}' | head -n 1); if [ -n \"$PID\" ]; then kill -9 $PID; fi") - .output() - .await?; - - if !kill_cmd.status.success() { - println!("Failed to kill running movement process: {:?}", kill_cmd.stderr); - } else { - println!("Movement process killed if it was running."); - } - - let delete_dir_cmd = TokioCommand::new("sh") - .arg("-c") - .arg("if [ -d '.movement' ]; then rm -rf .movement; fi") - .output() - .await?; - - if !delete_dir_cmd.status.success() { - println!("Failed to delete .movement directory: {:?}", delete_dir_cmd.stderr); - } else { - println!(".movement directory deleted if it was present."); - } - - let (setup_complete_tx, setup_complete_rx) = oneshot::channel(); - let mut child = TokioCommand::new("movement") - .args(&["node", "run-local-testnet", "--force-restart", "--assume-yes"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - - let stdout = child.stdout.take().expect("Failed to capture stdout"); - let stderr = child.stderr.take().expect("Failed to capture stderr"); - - task::spawn(async move { - let mut stdout_reader = BufReader::new(stdout).lines(); - let mut stderr_reader = BufReader::new(stderr).lines(); - - loop { - tokio::select! { - line = stdout_reader.next_line() => { - match line { - Ok(Some(line)) => { - println!("STDOUT: {}", line); - if line.contains("Setup is complete") { - println!("Testnet is up and running!"); - let _ = setup_complete_tx.send(()); - return Ok(()); - } - }, - Ok(_) => { - return Err(anyhow::anyhow!("Unexpected end of stdout stream")); - }, - Err(e) => { - return Err(anyhow::anyhow!("Error reading stdout: {}", e)); - } - } - }, - line = stderr_reader.next_line() => { - match line { - Ok(Some(line)) => { - println!("STDERR: {}", line); - if line.contains("Setup is complete") { - println!("Testnet is up and running!"); - let _ = setup_complete_tx.send(()); - return Ok(()); - } - }, - Ok(_) => { - return Err(anyhow::anyhow!("Unexpected end of stderr stream")); - } - Err(e) => { - return Err(anyhow::anyhow!("Error reading stderr: {}", e)); - } - } - } - } - } - }); - - setup_complete_rx.await.expect("Failed to receive setup completion signal"); - println!("Setup complete message received."); - - let node_connection_url = "http://127.0.0.1:8080".to_string(); - let node_connection_url = Url::from_str(node_connection_url.as_str()) - .map_err(|_| BridgeContractError::SerializationError)?; - let rest_client = Client::new(node_connection_url.clone()); - - let mut rng = ::rand::rngs::StdRng::from_seed([3u8; 32]); - Ok(( - MovementClientFramework { - native_address: DUMMY_ADDRESS, - rest_client, - signer: Arc::new(LocalAccount::generate(&mut rng)), - }, - child, - )) + async fn is_bridge_transfer_completed( + &mut self, + bridge_transfer_id: BridgeTransferId, + ) -> BridgeContractResult { + todo!() } } diff --git a/protocol-units/bridge/service/src/chains/movement/event_monitoring.rs b/protocol-units/bridge/service/src/chains/movement/event_monitoring.rs index 9c0c6cceb..5089f3b5f 100644 --- a/protocol-units/bridge/service/src/chains/movement/event_monitoring.rs +++ b/protocol-units/bridge/service/src/chains/movement/event_monitoring.rs @@ -1,22 +1,20 @@ -use super::{ - client_framework::{MovementClientFramework, FRAMEWORK_ADDRESS}, - utils::MovementAddress, -}; -use crate::{ - chains::bridge_contracts::{ - BridgeContractError, BridgeContractEvent, BridgeContractEventType, - BridgeContractMonitoring, BridgeContractResult, - }, - types::{ - Amount, BridgeAddress, BridgeTransferDetails, BridgeTransferId, HashLock, HashLockPreImage, - LockDetails, TimeLock, - }, -}; +use super::{client_framework::FRAMEWORK_ADDRESS, utils::MovementAddress}; +use crate::types::{Amount, BridgeAddress, BridgeTransferId}; use anyhow::Result; use aptos_sdk::{ rest_client::aptos_api_types::VersionedEvent, types::account_address::AccountAddress, }; use bridge_config::common::movement::MovementConfig; +use bridge_util::chains::bridge_contracts::BridgeContractError; +use bridge_util::chains::bridge_contracts::BridgeContractEventType; +use bridge_util::chains::bridge_contracts::BridgeContractResult; +use bridge_util::chains::bridge_contracts::BridgeTransferCompletedDetails; +use bridge_util::chains::bridge_contracts::BridgeTransferInitiatedDetails; +use bridge_util::types::Nonce; +use bridge_util::BridgeContractEvent; +use bridge_util::BridgeContractMonitoring; +use std::sync::Arc; +use tokio::sync::RwLock; use futures::{ channel::mpsc::{self as futurempsc}, @@ -34,12 +32,8 @@ const PULL_STATE_FILE_NAME: &str = "pullstate.store"; #[derive(Debug, Default, Clone, Serialize, Deserialize)] struct MvtPullingState { - initiator_init: u64, - initiator_complete: u64, - initiator_refund: u64, - counterpart_lock: u64, - counterpart_complete: u64, - counterpart_cancel: u64, + initiated: u64, + completed: u64, } impl MvtPullingState { @@ -86,33 +80,13 @@ impl MvtPullingState { //define the state to the next event. match event { BridgeContractEvent::Initiated(_) => { - if self.initiator_init <= sequence_number { - self.initiator_init = sequence_number + 1 - } - } - BridgeContractEvent::Locked(_) => { - if self.counterpart_lock <= sequence_number { - self.counterpart_lock = sequence_number + 1 - } - } - BridgeContractEvent::InitiatorCompleted(_) => { - if self.initiator_complete <= sequence_number { - self.initiator_complete = sequence_number + 1 + if self.initiated <= sequence_number { + self.initiated = sequence_number + 1 } } - BridgeContractEvent::CounterPartyCompleted(_, _) => { - if self.counterpart_complete <= sequence_number { - self.counterpart_complete = sequence_number + 1 - } - } - BridgeContractEvent::Cancelled(_) => { - if self.counterpart_cancel <= sequence_number { - self.counterpart_cancel = sequence_number + 1 - } - } - BridgeContractEvent::Refunded(_) => { - if self.initiator_refund <= sequence_number { - self.initiator_refund = sequence_number + 1 + BridgeContractEvent::Completed(_) => { + if self.completed <= sequence_number { + self.completed = sequence_number + 1 } } } @@ -123,12 +97,8 @@ impl MvtPullingState { fn update_state_with_error(&mut self, err: &BridgeContractError) { match err { BridgeContractError::EventDeserializingFail(_, event_type) => match event_type { - BridgeContractEventType::Initiated => self.initiator_init += 1, - BridgeContractEventType::Locked => self.counterpart_lock += 1, - BridgeContractEventType::InitiatorCompleted => self.initiator_complete += 1, - BridgeContractEventType::CounterPartyCompleted => self.counterpart_complete += 1, - BridgeContractEventType::Cancelled => self.counterpart_cancel += 1, - BridgeContractEventType::Refunded => self.initiator_refund += 1, + BridgeContractEventType::Initiated => self.initiated += 1, + BridgeContractEventType::Completed => self.completed += 1, }, _ => (), } @@ -136,29 +106,95 @@ impl MvtPullingState { } pub struct MovementMonitoring { + pulling_task: Option, listener: futurempsc::UnboundedReceiver>>, } +impl MovementMonitoring { + pub async fn build( + config: &MovementConfig, + health_check_rx: mpsc::Receiver>, + ) -> Result { + let pulling_task = PullMonitoring::start_pulling(config, health_check_rx).await?; + let listener = pulling_task.add_notification_channel().await; + + Ok(Self { pulling_task: Some(pulling_task), listener }) + } + + pub async fn child(&self) -> Self { + let listener = self + .pulling_task + .as_ref() + .expect("EthMonitoring Clone on non initial object.") + .add_notification_channel() + .await; + Self { pulling_task: None, listener } + } +} + impl BridgeContractMonitoring for MovementMonitoring { type Address = MovementAddress; } -impl MovementMonitoring { - pub async fn build( +impl Stream for MovementMonitoring { + type Item = BridgeContractResult>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { + let this = self.get_mut(); + this.listener.poll_next_unpin(cx) + } +} + +pub struct PullMonitoring { + notification_channel_list: Arc< + RwLock< + Vec< + futurempsc::UnboundedSender< + BridgeContractResult>, + >, + >, + >, + >, +} +impl PullMonitoring { + pub async fn add_notification_channel( + &self, + ) -> futurempsc::UnboundedReceiver>> { + let (sender, listener) = futures::channel::mpsc::unbounded::< + BridgeContractResult>, + >(); + let mut list = self.notification_channel_list.write().await; + list.push(sender); + listener + } + + async fn notify_event( + list: &Vec< + futurempsc::UnboundedSender>>, + >, + event: BridgeContractResult>, + ) { + for ref mut notif in list { + if notif.send(event.clone()).await.is_err() { + tracing::error!("MvtMonitor Failed to send event to listener channel"); + break; + } + } + } + + pub async fn start_pulling( config: &MovementConfig, mut health_check_rx: mpsc::Receiver>, ) -> Result { - // Spawn a task to forward events to the listener channel - let (mut sender, listener) = futures::channel::mpsc::unbounded::< - BridgeContractResult>, - >(); + let notification_channel_list = Arc::new(RwLock::new(vec![])); //read the pull state let mut pull_state = MvtPullingState::build_from_store_file().await?; tokio::spawn({ let config = config.clone(); + let notification_channel_list = notification_channel_list.clone(); async move { loop { //Check if there's a health check request @@ -176,18 +212,7 @@ impl MovementMonitoring { } } - let mut init_event_list = match pool_initiator_contract( - FRAMEWORK_ADDRESS, - &config.mvt_rpc_connection_url(), - &pull_state, - config.rest_connection_timeout_secs, - ) - .await - { - Ok(evs) => evs.into_iter().map(|ev| Ok(ev)).collect(), - Err(err) => vec![Err(err)], - }; - let mut counterpart_event_list = match pool_counterparty_contract( + let mut init_event_list = match pool_contract( FRAMEWORK_ADDRESS, &config.mvt_rpc_connection_url(), &pull_state, @@ -200,29 +225,29 @@ impl MovementMonitoring { }; //extract event sequence_number and update pull state - let (event_list, new_pull_state) = - init_event_list.drain(..).chain(counterpart_event_list.drain(..)).fold( - (Vec::new(), pull_state.clone()), - |(mut events, mut state), event| { - match event { - Ok((ev, seq)) => { - state.update_state_with_event(&ev, seq); - events.push(Ok(ev)); - } - Err(err) => { - state.update_state_with_error(&err); - events.push(Err(err)); - } + let (event_list, new_pull_state) = init_event_list.drain(..).fold( + (Vec::new(), pull_state.clone()), + |(mut events, mut state), event| { + match event { + Ok((ev, seq)) => { + state.update_state_with_event(&ev, seq); + events.push(Ok(ev)); } - (events, state) - }, - ); + Err(err) => { + state.update_state_with_error(&err); + events.push(Err(err)); + } + } + (events, state) + }, + ); for event in event_list { - if sender.send(event).await.is_err() { - tracing::error!("Failed to send event to listener channel"); - break; - } + PullMonitoring::notify_event( + &*notification_channel_list.read().await, + event, + ) + .await; } pull_state = new_pull_state; @@ -234,16 +259,7 @@ impl MovementMonitoring { } }); - Ok(MovementMonitoring { listener }) - } -} - -impl Stream for MovementMonitoring { - type Item = BridgeContractResult>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { - let this = self.get_mut(); - this.listener.poll_next_unpin(cx) + Ok(PullMonitoring { notification_channel_list }) } } @@ -252,35 +268,31 @@ struct CounterpartyCompletedDetails { pub bridge_transfer_id: BridgeTransferId, pub initiator: BridgeAddress>, pub recipient: BridgeAddress, - pub hash_lock: HashLock, - pub secret: HashLockPreImage, pub amount: Amount, + pub nonce: Nonce, } -async fn pool_initiator_contract( +async fn pool_contract( framework_address: AccountAddress, rest_url: &str, pull_state: &MvtPullingState, timeout_sec: u64, ) -> BridgeContractResult, u64)>> { - let struct_tag = format!( - "{}::atomic_bridge_initiator::BridgeInitiatorEvents", - framework_address.to_string() - ); + let struct_tag = format!("{}::native_bridge::BridgeEvents", framework_address.to_string()); // Get initiated events let initiated_events = get_account_events( rest_url, &framework_address.to_string(), &struct_tag, "bridge_transfer_initiated_events", - pull_state.initiator_init, + pull_state.initiated, timeout_sec, ) .await? .into_iter() .map(|e| { - let data: BridgeInitEventData = serde_json::from_str(&e.data.to_string())?; - let transfer_details = BridgeTransferDetails::try_from(data)?; + let data: BridgeEventData = serde_json::from_str(&e.data.to_string())?; + let transfer_details = BridgeTransferInitiatedDetails::try_from(data)?; Ok((BridgeContractEvent::Initiated(transfer_details), e.sequence_number.into())) }) .collect::>>() @@ -297,201 +309,41 @@ async fn pool_initiator_contract( &framework_address.to_string(), &struct_tag, "bridge_transfer_completed_events", - pull_state.initiator_complete, + pull_state.completed, timeout_sec, ) .await? .into_iter() .map(|e| { - let data: BridgeCompletEventData = serde_json::from_str(&e.data.to_string())?; - let event = BridgeContractEvent::InitiatorCompleted( - data.bridge_transfer_id.try_into().map_err(|err| { - BridgeContractError::ConversionFailed(format!( - "MVT initiatorbridge_transfer_completed_events bridge_transfer_id can't be reconstructed:{:?}", - err - )) - })?, - ); - Ok((event, e.sequence_number.into())) + let data: BridgeEventData = serde_json::from_str(&e.data.to_string())?; + let transfer_details = BridgeTransferCompletedDetails::try_from(data)?; + Ok((BridgeContractEvent::Completed(transfer_details), e.sequence_number.into())) }) .collect::>>() .map_err(|e| { BridgeContractError::EventDeserializingFail( format!("MVT bridge_transfer_completed_events de-serialization error:{}", e), - BridgeContractEventType::InitiatorCompleted, - ) - })?; - - // Get refunded events - let refunded_events = get_account_events( - rest_url, - &framework_address.to_string(), - &struct_tag, - "bridge_transfer_refunded_events", - pull_state.initiator_refund, - timeout_sec, - ) - .await? - .into_iter() - .map(|e| { - let data = deserialize_hex_vec(e.data)?; - let event = BridgeContractEvent::Refunded(data.try_into().map_err(|err| { - BridgeContractError::ConversionFailed(format!( - "MVT bridge_transfer_refunded_events bridge_transfer_id can't be reconstructed:{:?}", - err - )) - })?); - Ok((event, e.sequence_number.into())) - }) - .collect::>>() - .map_err(|e| { - BridgeContractError::EventDeserializingFail( - format!("MVT bridge_transfer_refunded_events de-serialization error:{}", e), - BridgeContractEventType::Refunded, + BridgeContractEventType::Completed, ) })?; let total_events = initiated_events .into_iter() .chain(completed_events.into_iter()) - .chain(refunded_events.into_iter()) .collect::>(); Ok(total_events) } -async fn pool_counterparty_contract( - framework_address: AccountAddress, - rest_url: &str, - pull_state: &MvtPullingState, - timeout_sec: u64, -) -> BridgeContractResult, u64)>> { - let struct_tag = format!( - "{}::atomic_bridge_counterparty::BridgeCounterpartyEvents", - FRAMEWORK_ADDRESS.to_string() - ); - - // Get locked events - let locked_events = get_account_events( - rest_url, - &framework_address.to_string(), - &struct_tag, - "bridge_transfer_locked_events", - pull_state.counterpart_lock, - timeout_sec, - ) - .await? - .into_iter() - .map(|e| { - let data: BridgeInitEventData = serde_json::from_str(&e.data.to_string())?; - let transfer_details = LockDetails::try_from(data)?; - println!("Transfer details: {:?}", transfer_details); - Ok((BridgeContractEvent::Locked(transfer_details), e.sequence_number.into())) - }) - .collect::>>() - .map_err(|e| { - BridgeContractError::EventDeserializingFail( - format!("MVT bridge_transfer_locked_events de-serialization error:{}", e), - BridgeContractEventType::Locked, - ) - })?; - - // Get completed events - let completed_events = get_account_events( - rest_url, - &framework_address.to_string(), - &struct_tag, - "bridge_transfer_completed_events", - pull_state.counterpart_complete, - timeout_sec, - ) - .await? - .into_iter() - .map(|e| { - let data: BridgeCompletEventData = serde_json::from_str(&e.data.to_string())?; - let event = BridgeContractEvent::CounterPartyCompleted( - data.bridge_transfer_id.try_into().map_err(|err| { - BridgeContractError::ConversionFailed(format!( - "MVT counterparty bridge_transfer_completed_events bridge_transfer_id can't be reconstructed:{:?}", - err - )) - })?, - HashLockPreImage(data.pre_image.try_into().map_err(|err| { - BridgeContractError::ConversionFailed(format!( - "MVT counterparty bridge_transfer_completed_events pre_image can't be reconstructed:{:?}", - err - )) - })?), - ); - Ok((event, e.sequence_number.into())) - }) - .collect::>>() - .map_err(|e| { - BridgeContractError::EventDeserializingFail( - format!( - "MVT counterpart bridge_transfer_completed_events de-serialization error:{}", - e - ), - BridgeContractEventType::CounterPartyCompleted, - ) - })?; - - // Get cancelled events - let cancelled_events = get_account_events( - rest_url, - &framework_address.to_string(), - &struct_tag, - "bridge_transfer_cancelled_events", - pull_state.counterpart_cancel, - timeout_sec, - ) - .await? - .into_iter() - .map(|e| { - let data = deserialize_hex_vec(e.data)?; - let event = BridgeContractEvent::Cancelled(data.try_into().map_err(|err| { - BridgeContractError::ConversionFailed(format!( - "MVT bridge_transfer_cancelled_events bridge_transfer_id can't be reconstructed:{:?}", - err - )) - })?); - Ok((event, e.sequence_number.into())) - }) - .collect::>>() - .map_err(|e| { - BridgeContractError::EventDeserializingFail( - format!("MVT bridge_transfer_cancelled_events de-serialization error:{}", e), - BridgeContractEventType::Cancelled, - ) - })?; - - let total_events = locked_events - .into_iter() - .chain(completed_events.into_iter()) - .chain(cancelled_events.into_iter()) - .collect::>(); - Ok(total_events) -} - -#[derive(Debug, Deserialize)] -pub struct BridgeCompletEventData { - #[serde(deserialize_with = "deserialize_hex_vec")] - pub bridge_transfer_id: Vec, - #[serde(deserialize_with = "deserialize_hex_vec")] - pub pre_image: Vec, -} - #[derive(Debug, Deserialize)] -pub struct BridgeInitEventData { +pub struct BridgeEventData { #[serde(deserialize_with = "deserialize_hex_vec")] pub bridge_transfer_id: Vec, #[serde(deserialize_with = "deserialize_hex_vec")] pub initiator: Vec, #[serde(deserialize_with = "deserialize_hex_vec")] pub recipient: Vec, - #[serde(deserialize_with = "deserialize_hex_vec")] - pub hash_lock: Vec, - #[serde(deserialize_with = "deserialize_u64_from_string")] - pub time_lock: u64, + #[serde(deserialize_with = "deserialize_u128_from_string")] + pub nonce: u128, #[serde(deserialize_with = "deserialize_u64_from_string")] pub amount: u64, } @@ -506,6 +358,14 @@ where Vec::from_hex(hex_str).map_err(serde::de::Error::custom) } +fn deserialize_u128_from_string<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + s.parse::().map_err(serde::de::Error::custom) +} + fn deserialize_u64_from_string<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -514,11 +374,11 @@ where s.parse::().map_err(serde::de::Error::custom) } -impl TryFrom for BridgeTransferDetails { +impl TryFrom for BridgeTransferInitiatedDetails { type Error = BridgeContractError; - fn try_from(data: BridgeInitEventData) -> Result { - Ok(BridgeTransferDetails { + fn try_from(data: BridgeEventData) -> Result { + Ok(BridgeTransferInitiatedDetails { bridge_transfer_id: BridgeTransferId(data.bridge_transfer_id.try_into().map_err( |e| { BridgeContractError::ConversionFailed(format!( @@ -532,24 +392,17 @@ impl TryFrom for BridgeTransferDetails { .map_err(|err| BridgeContractError::OnChainError(err.to_string()))?, ), recipient: BridgeAddress(data.recipient), - hash_lock: HashLock(data.hash_lock.try_into().map_err(|e| { - BridgeContractError::ConversionFailed(format!( - "MVT BridgeTransferDetails data onchain hash_lock conversion error error:{:?}", - e - )) - })?), - time_lock: TimeLock(data.time_lock), + nonce: Nonce(data.nonce), amount: Amount(data.amount), - state: 0, }) } } -impl TryFrom for LockDetails { +impl TryFrom for BridgeTransferCompletedDetails { type Error = BridgeContractError; - fn try_from(data: BridgeInitEventData) -> Result { - Ok(LockDetails { + fn try_from(data: BridgeEventData) -> Result { + Ok(BridgeTransferCompletedDetails { bridge_transfer_id: BridgeTransferId(data.bridge_transfer_id.try_into().map_err( |e| { BridgeContractError::ConversionFailed(format!( @@ -563,13 +416,7 @@ impl TryFrom for LockDetails { MovementAddress::try_from(data.recipient) .map_err(|err| BridgeContractError::OnChainError(err.to_string()))?, ), - hash_lock: HashLock(data.hash_lock.try_into().map_err(|e| { - BridgeContractError::ConversionFailed(format!( - "MVT BridgeTransferDetails data onchain hash_lock conversion error error:{:?}", - e - )) - })?), - time_lock: TimeLock(data.time_lock), + nonce: Nonce(data.nonce), amount: Amount(data.amount), }) } diff --git a/protocol-units/bridge/service/src/chains/movement/utils.rs b/protocol-units/bridge/service/src/chains/movement/utils.rs index 662c25e28..625323cd8 100644 --- a/protocol-units/bridge/service/src/chains/movement/utils.rs +++ b/protocol-units/bridge/service/src/chains/movement/utils.rs @@ -23,7 +23,7 @@ use aptos_sdk::{ }; use bridge_util::{ chains::bridge_contracts::BridgeContractError, - types::{AddressError, BridgeAddress, HashLockPreImage}, + types::{AddressError, BridgeAddress}, }; use derive_new::new; use rand::{rngs::StdRng, Rng, RngCore, SeedableRng}; @@ -130,26 +130,6 @@ impl TryFrom<&str> for MovementAddress { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct MovementHash(pub [u8; 32]); - -impl MovementHash { - pub fn random() -> Self { - let mut rng = TestRng::seed_from_u64(0); - let mut hash = [0u8; 32]; - rng.fill_bytes(&mut hash); - Self(hash) - } -} - -impl From for MovementHash { - fn from(preimage: HashLockPreImage) -> Self { - let mut hash = [0u8; 32]; - hash.copy_from_slice(&preimage.0); - Self(hash) - } -} - /// limit of gas unit const GAS_UNIT_LIMIT: u64 = 100000; /// minimum price of gas unit of aptos chains @@ -195,7 +175,7 @@ pub async fn send_and_confirm_aptos_transaction( let signed_tx = signer.sign_transaction(raw_tx); - //info!("Signed TX: {:?}", signed_tx); + info!("Signed TX: {:?}", signed_tx); let response = rest_client.submit_and_wait(&signed_tx).await.map_err(|e| { let err_msg = format!("Transaction submission error: {}", e.to_string()); @@ -204,7 +184,7 @@ pub async fn send_and_confirm_aptos_transaction( })?; let txn = response.into_inner(); - //info!("Response: {:?}", txn); + info!("Response: {:?}", txn); match &txn { Transaction::UserTransaction(user_txn) => { @@ -278,11 +258,15 @@ pub fn serialize_vec( bcs::to_bytes(value).map_err(|_| BridgeContractError::SerializationError) } -pub fn serialize_u64_initiator(value: &u64) -> Result, BridgeContractError> { - bcs::to_bytes(value).map_err(|_| BridgeContractError::SerializationError) +pub fn serialize_u64_initiator(value: u64) -> Result, BridgeContractError> { + bcs::to_bytes(&value).map_err(|_| BridgeContractError::SerializationError) +} + +pub fn serialize_u128_initiator(value: u128) -> Result, BridgeContractError> { + bcs::to_bytes(&value).map_err(|_| BridgeContractError::SerializationError) } -pub fn serialize_address_initiator( +pub fn serialize_address( address: &AccountAddress, ) -> Result, BridgeContractError> { bcs::to_bytes(address).map_err(|_| BridgeContractError::SerializationError) diff --git a/protocol-units/bridge/service/src/lib.rs b/protocol-units/bridge/service/src/lib.rs index 789483e42..27a61e3fb 100644 --- a/protocol-units/bridge/service/src/lib.rs +++ b/protocol-units/bridge/service/src/lib.rs @@ -1,19 +1,3 @@ -use crate::actions::process_action; -use bridge_indexer_db::client::Client as IndexerClient; -use bridge_util::{ - actions::{ActionExecError, TransferAction, TransferActionType}, - chains::bridge_contracts::{BridgeContract, BridgeContractEvent, BridgeContractMonitoring}, - events::{InvalidEventError, TransferEvent}, - states::{TransferState, TransferStateType}, - types::{BridgeTransferId, ChainId}, -}; -use futures::stream::FuturesUnordered; -use std::{collections::HashMap, sync::Arc}; -use tokio::sync::mpsc; -use tokio::sync::oneshot; -use tokio::{select, sync::Mutex}; -use tokio_stream::StreamExt; - pub use bridge_util::types; mod actions; @@ -21,464 +5,5 @@ pub mod chains; pub mod grpc; pub mod rest; -#[derive(Debug)] -struct HeathCheckStatus { - chain_one: bool, - chain_two: bool, -} - -impl HeathCheckStatus { - fn new() -> Self { - HeathCheckStatus { chain_one: true, chain_two: true } - } - fn check(&self) -> bool { - self.chain_one && self.chain_two - } - - fn update_state(&mut self, chain: ChainId, state: bool) { - match chain { - ChainId::ONE => self.chain_one = state, - ChainId::TWO => self.chain_two = state, - } - } -} - -pub async fn run_bridge< - A1: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, - A2: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, ->( - client_one: impl BridgeContract + 'static, - mut stream_one: impl BridgeContractMonitoring
, - client_two: impl BridgeContract + 'static, - mut stream_two: impl BridgeContractMonitoring
, - mut healthcheck_request_rx: mpsc::Receiver>, - indexer_db_client: Option, - healthcheck_tx_one: mpsc::Sender>, - healthcheck_tx_two: mpsc::Sender>, -) -> Result<(), anyhow::Error> -where - Vec: From, - Vec: From, -{ - let mut state_runtime = Runtime::new(indexer_db_client); - - let mut client_exec_result_futures_one = FuturesUnordered::new(); - let mut client_exec_result_futures_two = FuturesUnordered::new(); - let mut health_check_result_futures = FuturesUnordered::new(); - - //only one client can use at a time. - let client_lock_one = Arc::new(Mutex::new(())); - let client_lock_two = Arc::new(Mutex::new(())); - - let mut tranfer_log_interval = tokio::time::interval(tokio::time::Duration::from_secs(60)); - let mut monitoring_health_check_interval = - tokio::time::interval(tokio::time::Duration::from_secs(5)); - - let mut health_status = HeathCheckStatus::new(); - - loop { - select! { - //Manage REST HealthCheck request - Some(oneshot_tx) = healthcheck_request_rx.recv() => { - let res = if health_status.check() { - "OK".to_string() - } else { - format!("NOK : {health_status:?}") - }; - if let Err(err) = oneshot_tx.send(res){ - tracing::warn!("Heal check oneshot channel closed abnormally :{err:?}"); - } - - } - // verify that monitoring heath check still works. - _ = monitoring_health_check_interval.tick() => { - //Chain one monitoring health check. - let jh = tokio::spawn({ - let healthcheck_tx = healthcheck_tx_one.clone(); - async move { - (ChainId::ONE, check_monitoring_loop_heath(healthcheck_tx).await) - } - }); - health_check_result_futures.push(jh); - //Chain two monitoring health check. - let jh = tokio::spawn({ - let healthcheck_tx = healthcheck_tx_two.clone(); - async move { - (ChainId::TWO, check_monitoring_loop_heath(healthcheck_tx).await) - } - }); - health_check_result_futures.push(jh); - } - // Process health check result. - Some(res) = health_check_result_futures.next() => { - match res { - //Client execution ok. - Ok((chain, Ok(status))) => health_status.update_state(chain, status), - Ok((chain, Err(err))) => { - tracing::warn!("Chain {chain} monitoring health check fail with an error:{err}",); - health_status.update_state(chain, false); - }, - Err(err)=>{ - // Tokio execution fail. Process should exit. - tracing::error!("Error during health check tokio task execution exiting: {err}"); - return Err(err.into()); - } - } - } - // Log all current transfer - _ = tranfer_log_interval.tick() => { - //format logs - let logs: Vec<_> = state_runtime.iter_state().map(|state| state.to_string()).collect(); - tokio::spawn(async move { - tracing::info!("Bridge current transfer processing:{:#?}", logs); - }); - } - // Wait on chain one events. - Some(event_res_one) = stream_one.next() =>{ - match event_res_one { - Ok(event_one) => { - let event : TransferEvent = (event_one, ChainId::ONE).into(); - tracing::info!("Receive event from chain ONE:{} ", event.contract_event); - match state_runtime.process_event(event) { - Ok(action) => { - //Execute action - match action.chain { - ChainId::ONE => { - let fut = process_action(action, client_one.clone()); - if let Some(fut) = fut { - let jh = tokio::spawn({ - let client_lock_clone = client_lock_one.clone(); - async move { - let _lock = client_lock_clone.lock().await; - fut.await - } - }); - client_exec_result_futures_one.push(jh); - } - - }, - ChainId::TWO => { - let fut = process_action(action, client_two.clone()); - if let Some(fut) = fut { - let jh = tokio::spawn({ - let client_lock_clone = client_lock_two.clone(); - async move { - let _lock = client_lock_clone.lock().await; - fut.await - } - }); - client_exec_result_futures_two.push(jh); - } - } - } - }, - Err(err) => tracing::warn!("Received an invalid event: {err}"), - } - } - Err(err) => tracing::error!("Chain one event stream return an error:{err}"), - } - } - // Wait on chain two events. - Some(event_res_two) = stream_two.next() =>{ - match event_res_two { - Ok(event_two) => { - let event : TransferEvent = (event_two, ChainId::TWO).into(); - tracing::info!("Receive event from chain TWO :{}", event.contract_event); - match state_runtime.process_event(event) { - Ok(action) => { - //Execute action - match action.chain { - ChainId::ONE => { - let fut = process_action(action, client_one.clone()); - if let Some(fut) = fut { - let jh = tokio::spawn(fut); - client_exec_result_futures_one.push(jh); - } - - }, - ChainId::TWO => { - let fut = process_action(action, client_two.clone()); - if let Some(fut) = fut { - let jh = tokio::spawn(fut); - client_exec_result_futures_two.push(jh); - } - } - } - }, - Err(err) => tracing::warn!("Received an invalid event: {err}"), - } - } - Err(err) => tracing::error!("Chain two event stream return an error:{err}"), - } - } - // Wait on client tx execution result. - Some(res) = client_exec_result_futures_one.next() => { - match res { - //Client execution ok. - Ok(Ok(_)) => (), - Ok(Err(err)) => { - // Manage Tx execution error - let action = state_runtime.process_action_exec_error(err); - // TODO execute action the same way as normal event. - // TODO refactor to avopid code duplication. - } - Err(err)=>{ - // Tokio execution fail. Process should exit. - tracing::error!("Error during client tokio tasj execution exiting: {err}"); - return Err(err.into()); - } - } - } - Some(res) = client_exec_result_futures_two.next() => { - match res { - //Client execution ok. - Ok(Ok(_)) => (), - Ok(Err(err)) => { - // Manage Tx execution error - let action = state_runtime.process_action_exec_error(err); - // TODO execute action the same way as normal event. - // TODO refactor to avopid code duplication. - } - Err(err)=>{ - // Tokio execution fail. Process should exit. - tracing::error!("Error during client tokio task execution exiting: {err}"); - return Err(err.into()); - } - } - } - } - } -} - -async fn check_monitoring_loop_heath( - healthcheck_tx: mpsc::Sender>, -) -> Result { - let (tx, rx) = oneshot::channel(); - healthcheck_tx - .send(tx) - .await - .map_err(|err| format!("Chain one Health check send error: {}", err))?; - let res = match tokio::time::timeout(tokio::time::Duration::from_secs(5), rx).await { - Ok(Ok(res)) => res, - Ok(Err(err)) => { - tracing::warn!("Chain one monitoring health check return an error:{err}"); - false - } - Err(_) => { - tracing::warn!("Chain one monitoring health check timeout. Monitoring is idle."); - false - } - }; - Ok(res) -} - -struct Runtime { - swap_state_map: HashMap, - indexer_db_client: Option, -} - -impl Runtime { - pub fn new(indexer_db_client: Option) -> Self { - Runtime { swap_state_map: HashMap::new(), indexer_db_client } - } - - pub fn iter_state(&self) -> impl Iterator { - self.swap_state_map.values() - } - - fn index_event(&mut self, event: TransferEvent) -> Result<(), InvalidEventError> - where - A: Into> + std::clone::Clone + std::fmt::Debug, - { - match self.indexer_db_client { - Some(ref mut client) => { - let event = event.contract_event; - - client.insert_bridge_contract_event(event.clone()).map_err(|err| { - tracing::warn!("Fail to index event :{err}"); - InvalidEventError::IndexingFailed(err.to_string()) - })?; - tracing::info!("index_event(success):{event:?}"); - Ok(()) - } - None => { - tracing::warn!("No indexer db client found. Event not indexed"); - Ok(()) - } - } - } - - pub fn index_transfer_action( - &mut self, - action: TransferAction, - ) -> Result<(), InvalidEventError> { - match self.indexer_db_client { - Some(ref mut client) => { - let action = action.kind; - client.insert_transfer_action(action.clone()).map_err(|err| { - tracing::warn!("Fail to index action"); - InvalidEventError::BadEvent(err.to_string()) - })?; - tracing::info!("index_transfer_action(success): {action:?}"); - Ok(()) - } - None => { - tracing::warn!("No indexer db client found. Action not indexed"); - Ok(()) - } - } - } - - pub fn process_event( - &mut self, - event: TransferEvent, - ) -> Result - where - A: Into> + std::clone::Clone + std::fmt::Debug, - { - tracing::info!("Event received: {:?}", event); - self.validate_state(&event)?; - let indexer_event = event.clone(); - self.index_event(indexer_event)?; - let event_transfer_id = event.contract_event.bridge_transfer_id(); - let state_opt = self.swap_state_map.remove(&event_transfer_id); - //create swap state if need - let mut state = if let BridgeContractEvent::Initiated(detail) = event.contract_event { - let (state, mut action) = - TransferState::transition_from_initiated(event.chain, event_transfer_id, detail); - action.chain = state.init_chain.other(); - self.swap_state_map.insert(state.transfer_id, state); - self.index_transfer_action(action.clone())?; - return Ok(action); - } else { - //tested before in validate_state() state can be unwrap - state_opt.unwrap() - }; - - let (action_kind, chain_id) = match event.contract_event { - BridgeContractEvent::Initiated(_) => unreachable!(), - BridgeContractEvent::Locked(detail) => { - let (new_state, action_kind) = - state.transition_from_locked_done(event_transfer_id, detail); - state = new_state; - (action_kind, state.init_chain) - } - BridgeContractEvent::CounterPartyCompleted(_, preimage) => { - let (new_state, action_kind) = - state.transition_from_counterpart_completed(event_transfer_id, preimage); - state = new_state; - (action_kind, state.init_chain) - } - BridgeContractEvent::InitiatorCompleted(_) => { - let (new_state, action_kind) = - state.transition_from_initiator_completed(event_transfer_id); - state = new_state; - - (action_kind, state.init_chain) - } - BridgeContractEvent::Cancelled(_) => { - let (new_state, action_kind) = state.transition_from_cancelled(event_transfer_id); - state = new_state; - - (action_kind, state.init_chain) - } - BridgeContractEvent::Refunded(_) => { - let (new_state, action_kind) = state.transition_from_refunded(event_transfer_id); - state = new_state; - - (action_kind, state.init_chain) - } - }; - - let action = - TransferAction { chain: chain_id, transfer_id: state.transfer_id, kind: action_kind }; - - // index action - // todo: really this should come after process_action completion, but the current use of process_action is hacky - self.index_transfer_action(action.clone())?; - - if state.state != TransferStateType::Done { - self.swap_state_map.insert(state.transfer_id, state); - } - Ok(action) - } - - fn validate_state( - &mut self, - event: &TransferEvent, - ) -> Result<(), InvalidEventError> { - let event_transfer_id = event.contract_event.bridge_transfer_id(); - tracing::info!("Validating event with transfer ID: {:?}", event_transfer_id); - let swap_state_opt = self.swap_state_map.get(&event_transfer_id); - - // Log the current state if it exists in the swap state map - if let Some(state) = swap_state_opt { - tracing::info!( - "Found existing state for transfer ID {:?}: {:?}", - event_transfer_id, - state.state - ); - } else { - tracing::info!("No existing state found for transfer ID {:?}", event_transfer_id); - } - //validate the associated swap_state. - swap_state_opt - .as_ref() - //if the state is present validate the event is compatible - .map(|state| state.validate_event(&event)) - //if not validate the event is BridgeContractEvent::Initiated - .or_else(|| { - Some( - (swap_state_opt.is_none() && event.contract_event.is_initiated_event()) - .then_some(()) - .ok_or(InvalidEventError::StateNotFound), - ) - }) - .transpose()?; - Ok(()) - } - - fn process_action_exec_error(&mut self, action_err: ActionExecError) -> Option { - // Manage Tx execution error - let (action, err) = action_err.inner(); - tracing::warn!("Client execution error for action:{action} err:{err}"); - // retry 5 time an action in error then abort. - match self.swap_state_map.get_mut(&action.transfer_id) { - Some(state) => { - state.retry_on_error += 1; - if state.retry_on_error > 5 { - // Depending on the action cancel transfer - match action.kind { - TransferActionType::LockBridgeTransfer { .. } => { - //Lock fail. Refund initiator - let (new_state_type, action_kind) = state.transition_to_refund(); - state.state = new_state_type; - let action = TransferAction { - chain: state.init_chain, - transfer_id: state.transfer_id, - kind: action_kind, - }; - Some(action) - } - TransferActionType::WaitAndCompleteInitiator(..) => { - todo!() - } - TransferActionType::RefundInitiator => None, //will wait automatic refund - TransferActionType::TransferDone => None, - TransferActionType::NoAction => None, - } - } else { - //Rerun the action. - Some(action) - } - } - None => { - tracing::warn!( - "Receive an error for action but no state found for id:{:?}", - action.transfer_id - ); - None - } - } - } -} +pub mod relayer; +pub mod runtime; diff --git a/protocol-units/bridge/service/src/main.rs b/protocol-units/bridge/service/src/main.rs index d2cfb73a3..2a22eb399 100644 --- a/protocol-units/bridge/service/src/main.rs +++ b/protocol-units/bridge/service/src/main.rs @@ -3,7 +3,8 @@ use bridge_config::Config; use bridge_grpc::{ bridge_server::BridgeServer, health_check_response::ServingStatus, health_server::HealthServer, }; -use bridge_indexer_db::client::Client; +use bridge_util::chains::check_monitoring_health; +//use bridge_indexer_db::client::Client; use bridge_service::{ chains::{ ethereum::{client::EthClient, event_monitoring::EthMonitoring}, @@ -43,15 +44,18 @@ async fn main() -> Result<()> { tracing::info!("Bridge config loaded: {bridge_config:?}"); - let (eth_health_tx, eth_health_rx) = tokio::sync::mpsc::channel(10); - let one_stream = EthMonitoring::build(&bridge_config.eth, eth_health_rx).await.unwrap(); - let one_client = EthClient::new(&bridge_config.eth).await.unwrap(); - let two_client = MovementClientFramework::new(&bridge_config.movement).await.unwrap(); - let (mvt_health_tx, mvt_health_rx) = tokio::sync::mpsc::channel(10); - let two_stream = - MovementMonitoring::build(&bridge_config.movement, mvt_health_rx).await.unwrap(); + let (eth_client_health_tx, eth_client_health_rx) = tokio::sync::mpsc::channel(10); + let (mvt_client_health_tx, mvt_client_health_rx) = tokio::sync::mpsc::channel(10); + let eth_stream = EthMonitoring::build(&bridge_config.eth, eth_client_health_rx).await.unwrap(); + let eth_client = EthClient::build_with_config(&bridge_config.eth).await.unwrap(); + let mvt_client = MovementClientFramework::build_with_config(&bridge_config.movement) + .await + .unwrap(); + let mvt_stream = MovementMonitoring::build(&bridge_config.movement, mvt_client_health_rx) + .await + .unwrap(); - let one_client_for_grpc = one_client.clone(); + let eth_client_for_grpc = eth_client.clone(); // Initialize the gRPC health check service let health_service = HealthCheckService::default(); @@ -68,51 +72,66 @@ async fn main() -> Result<()> { let grpc_jh = tokio::spawn(async move { Server::builder() .add_service(HealthServer::new(health_service)) - .add_service(BridgeServer::new(one_client_for_grpc)) + .add_service(BridgeServer::new(eth_client_for_grpc)) .serve(grpc_addr) .await }); - // Initialize the gRPC health check service - let (health_tx, health_rx) = tokio::sync::mpsc::channel(10); - // Start the gRPC server on a specific address (e.g., localhost:50051) + // Initialize the Rpc health check service + let (eth_rest_health_tx, eth_rest_health_rx) = tokio::sync::mpsc::channel(10); + let (mvt_rest_health_tx, mvt_rest_health_rx) = tokio::sync::mpsc::channel(10); // Create and run the REST service - let rest_service = BridgeRest::new(&bridge_config.movement, health_tx)?; + let url = format!( + "{}:{}", + bridge_config.movement.rest_listener_hostname, bridge_config.movement.rest_port + ); + let rest_service = BridgeRest::new(url, eth_rest_health_tx, mvt_rest_health_tx)?; let rest_service_future = rest_service.run_service(); let rest_jh = tokio::spawn(rest_service_future); tracing::info!("Bridge Eth and Movement Inited. Starting bridge loop."); - let indexer_db_client = match Client::from_env() { - Ok(mut client) => { - client.run_migrations()?; - Some(client) - } - Err(e) => { - tracing::warn!("Failed to create indexer db client: {e:?}"); - None + + // Start Monitoring health check. + let eth_healh_check_jh = + tokio::spawn(check_monitoring_health("Eth", eth_client_health_tx, eth_rest_health_rx)); + let mvt_healh_check_jh = + tokio::spawn(check_monitoring_health("Mvt", mvt_client_health_tx, mvt_rest_health_rx)); + + // Start relay in L1-> L2 direction + let loop_jh1 = tokio::spawn({ + let eth_stream = eth_stream.child().await; + let mvt_stream = mvt_stream.child().await; + async move { + bridge_service::relayer::run_relayer_one_direction( + "Eth->Mvt", eth_stream, mvt_client, mvt_stream, + ) + .await } - }; + }); - let loop_jh = tokio::spawn(async move { - bridge_service::run_bridge( - one_client, - one_stream, - two_client, - two_stream, - health_rx, - indexer_db_client, - eth_health_tx, - mvt_health_tx, + // Start relay in L2-> L1 direction + let loop_jh2 = tokio::spawn(async move { + bridge_service::relayer::run_relayer_one_direction( + "Mvt->Eth", mvt_stream, eth_client, eth_stream, ) .await }); tokio::select! { + res = eth_healh_check_jh => { + tracing::error!("Heath check Eth monitoring exit because :{res:?}"); + } + res = mvt_healh_check_jh => { + tracing::error!("Heath check Eth monitoring exit because :{res:?}"); + } res = rest_jh => { tracing::error!("Heath check Rest server exit because :{res:?}"); } - res = loop_jh => { - tracing::error!("Main relayer loop exit because :{res:?}"); + res = loop_jh1 => { + tracing::error!("Eth->Mvt relayer loop exit because :{res:?}"); + } + res = loop_jh2 => { + tracing::error!("Mvt->Eth relayer loop exit because :{res:?}"); } res = grpc_jh => { tracing::error!("gRpc server exit because :{res:?}"); diff --git a/protocol-units/bridge/service/src/relayer.rs b/protocol-units/bridge/service/src/relayer.rs new file mode 100644 index 000000000..2a77467f0 --- /dev/null +++ b/protocol-units/bridge/service/src/relayer.rs @@ -0,0 +1,143 @@ +use crate::actions; +use crate::runtime::Runtime; +//use bridge_indexer_db::client::Client as IndexerClient; +use bridge_util::{ + actions::{ActionExecError, TransferAction}, + chains::bridge_contracts::{ + BridgeContractEvent, BridgeContractMonitoring, BridgeRelayerContract, + }, + events::TransferEvent, +}; +use futures::stream::FuturesUnordered; +use std::sync::Arc; +use tokio::{select, sync::Mutex}; +use tokio_stream::StreamExt; + +pub async fn run_relayer_one_direction< + SOURCE: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, + TARGET: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, +>( + direction: &str, + mut stream_source: impl BridgeContractMonitoring
, + client_target: impl BridgeRelayerContract + 'static, + mut stream_target: impl BridgeContractMonitoring
, +) -> Result<(), anyhow::Error> +where + Vec: From, + Vec: From, +{ + let mut state_runtime = Runtime::new(); //indexer_db_client + + let mut client_exec_result_futures = FuturesUnordered::new(); + + //only one client can use at a time. + let client_lock = Arc::new(Mutex::new(())); + + let mut transfer_log_interval = tokio::time::interval(tokio::time::Duration::from_secs(60)); + + loop { + select! { + // Wait on chain one events. + Some(event_res) = stream_source.next() =>{ + match event_res { + Ok(BridgeContractEvent::Initiated(detail)) => { + let event : TransferEvent = BridgeContractEvent::Initiated(detail).into(); + tracing::info!("Relayer:{direction}, receive Initiated event :{} ", event.contract_event); + process_event(event, &mut state_runtime, client_target.clone(), client_lock.clone(), &mut client_exec_result_futures); + } + Ok(_) => (), //do nothing for other event. + Err(err) => tracing::error!("Relayer:{direction} event stream return an error:{err}"), + } + } + Some(event_res) = stream_target.next() =>{ + match event_res { + Ok(BridgeContractEvent::Completed(detail)) => { + let event : TransferEvent = BridgeContractEvent::Completed(detail).into(); + tracing::info!("Relayer:{direction}, receive Completed event :{} ", event.contract_event); + process_event(event, &mut state_runtime, client_target.clone(), client_lock.clone(), &mut client_exec_result_futures); + } + Ok(_) => (), //do nothing for other event. + Err(err) => tracing::error!("Relayer:{direction} event stream return an error:{err}"), + } + } + // Wait on client tx execution result. + Some(res) = client_exec_result_futures.next() => { + match res { + //Client execution ok. + Ok(Ok(_)) => (), + Ok(Err(err)) => { + // Manage Tx execution error + if let Some(action) = state_runtime.process_action_exec_error(err) { + execute_action(action, &mut state_runtime, client_target.clone(), client_lock.clone(), &mut client_exec_result_futures); + } + } + Err(err)=>{ + // Tokio execution fail. Process should exit. + tracing::error!("Relayer:{direction}, Error during client tokio task execution exiting: {err}"); + return Err(err.into()); + } + } + } + // Log all current transfer + _ = transfer_log_interval.tick() => { + //format logs + let logs: Vec<_> = state_runtime.iter_state().map(|state| state.to_string()).collect(); + tokio::spawn({ + let direction = direction.to_string(); + async move { + tracing::info!("Relayer:{direction} current transfer processing:{:#?}", logs); + } + }); + } + } + } +} + +fn process_event< + A: std::clone::Clone + std::fmt::Debug, + TARGET: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, +>( + event: TransferEvent, + state_runtime: &mut Runtime, + client_target: impl BridgeRelayerContract + 'static, + tx_lock: Arc>, + client_exec_result_futures_one: &mut FuturesUnordered< + tokio::task::JoinHandle>, + >, +) where + Vec: From, +{ + match state_runtime.process_event(event) { + Ok(action) => execute_action( + action, + state_runtime, + client_target, + tx_lock, + client_exec_result_futures_one, + ), + Err(err) => tracing::warn!("Received an invalid event: {err}"), + } +} + +fn execute_action< + TARGET: Send + TryFrom> + std::clone::Clone + 'static + std::fmt::Debug, +>( + action: TransferAction, + state_runtime: &mut Runtime, + client_target: impl BridgeRelayerContract + 'static, + tx_lock: Arc>, + client_exec_result_futures_one: &mut FuturesUnordered< + tokio::task::JoinHandle>, + >, +) { + let fut = actions::process_action(action, state_runtime, client_target); + if let Some(fut) = fut { + let jh = tokio::spawn({ + async move { + let _lock = tx_lock.lock().await; + fut.await + } + }); + client_exec_result_futures_one.push(jh); + } +} diff --git a/protocol-units/bridge/service/src/rest.rs b/protocol-units/bridge/service/src/rest.rs index 8e7a781fe..e48c6a8d6 100644 --- a/protocol-units/bridge/service/src/rest.rs +++ b/protocol-units/bridge/service/src/rest.rs @@ -12,7 +12,8 @@ use tokio::sync::oneshot; use tracing::info; struct RestContext { - request_tx: mpsc::Sender>, + l1_request_tx: mpsc::Sender>, + l2_request_tx: mpsc::Sender>, } pub struct BridgeRest { @@ -24,13 +25,14 @@ impl BridgeRest { pub const BRIDGE_REST_ENV_VAR: &'static str = "BRIDGE_REST_URL"; pub fn new( - conf: &MovementConfig, - request_tx: mpsc::Sender>, + rest_listener_url: String, + l1_request_tx: mpsc::Sender>, + l2_request_tx: mpsc::Sender>, ) -> Result { - let url = format!("{}:{}", conf.rest_listener_hostname, conf.rest_port); + // let url = format!("{}:{}", conf.rest_listener_hostname, conf.rest_port); - let context = RestContext { request_tx }; - Ok(Self { url, context: Arc::new(context) }) + let context = RestContext { l1_request_tx, l2_request_tx }; + Ok(Self { url: rest_listener_url, context: Arc::new(context) }) } pub fn run_service(&self) -> impl Future> + Send { @@ -48,8 +50,12 @@ impl BridgeRest { #[handler] async fn health(context: Data<&Arc>) -> Result { - let (tx, rx) = oneshot::channel(); - tokio::time::timeout(std::time::Duration::from_secs(2), context.request_tx.send(tx)).await??; - let resp = rx.await?; - Ok(resp.into_response()) + let (l1_tx, l1_rx) = oneshot::channel(); + context.l1_request_tx.send(l1_tx).await?; + let (l2_tx, l2_rx) = oneshot::channel(); + context.l2_request_tx.send(l2_tx).await?; + let l1_resp = tokio::time::timeout(std::time::Duration::from_secs(2), l1_rx).await??; + let l2_resp = tokio::time::timeout(std::time::Duration::from_secs(2), l2_rx).await??; + let res = if l1_resp && l2_resp { "OK".to_string() } else { format!("NOK") }; + Ok(res.into_response()) } diff --git a/protocol-units/bridge/service/src/runtime.rs b/protocol-units/bridge/service/src/runtime.rs new file mode 100644 index 000000000..87b061e53 --- /dev/null +++ b/protocol-units/bridge/service/src/runtime.rs @@ -0,0 +1,133 @@ +//use bridge_indexer_db::client::Client as IndexerClient; +use bridge_util::{ + actions::{ActionExecError, TransferAction, TransferActionType}, + chains::bridge_contracts::BridgeContractEvent, + events::{InvalidEventError, TransferEvent}, + states::TransferState, + types::BridgeTransferId, +}; +use std::collections::HashMap; + +pub struct Runtime { + swap_state_map: HashMap, +} + +impl Runtime { + pub fn new() -> Self { + Runtime { swap_state_map: HashMap::new() } //indexer_db_client + } + + pub fn iter_state(&self) -> impl Iterator { + self.swap_state_map.values() + } + + pub fn remove_transfer(&mut self, transfer_id: BridgeTransferId) { + self.swap_state_map.remove(&transfer_id); + } + + pub fn process_event( + &mut self, + event: TransferEvent, + ) -> Result + where + A: Into> + std::clone::Clone + std::fmt::Debug, + { + self.validate_state(&event)?; + let event_transfer_id = event.contract_event.bridge_transfer_id(); + let state_opt = self.swap_state_map.remove(&event_transfer_id); + let (state, action) = match event.contract_event { + BridgeContractEvent::Initiated(detail) => { + TransferState::transition_from_initiated(event_transfer_id, detail) + } + BridgeContractEvent::Completed(_detail) => { + let state = state_opt.ok_or(InvalidEventError::BadEvent( + "Receive an invalid even after validation.".to_string(), + ))?; + let (new_state, action_kind) = state.transition_from_completed(event_transfer_id); + let action = + TransferAction { transfer_id: new_state.transfer_id, kind: action_kind }; + (new_state, action) + } + }; + self.swap_state_map.insert(state.transfer_id, state); + Ok(action) + } + + fn validate_state( + &mut self, + event: &TransferEvent, + ) -> Result<(), InvalidEventError> { + let event_transfer_id = event.contract_event.bridge_transfer_id(); + let swap_state_opt = self.swap_state_map.get(&event_transfer_id); + + // Log the current state if it exists in the swap state map + if let Some(state) = swap_state_opt { + tracing::info!( + "Found existing state for transfer ID {}: {}", + event_transfer_id, + state.state + ); + } else { + tracing::info!("No existing state found for transfer ID {}", event_transfer_id); + } + //validate the associated swap_state. + swap_state_opt + .as_ref() + //if the state is present validate the event is compatible + .map(|state| state.validate_event(&event)) + //if not validate the event is BridgeContractEvent::Initiated + .or_else(|| { + Some( + (swap_state_opt.is_none() && event.contract_event.is_initiated_event()) + .then_some(()) + .ok_or(InvalidEventError::StateNotFound), + ) + }) + .transpose()?; + Ok(()) + } + + pub fn process_action_exec_error( + &mut self, + action_err: ActionExecError, + ) -> Option { + // Manage Tx execution error + let (action, err) = action_err.inner(); + tracing::warn!("Client execution error for action:{action} err:{err}"); + // retry 5 time an action in error then abort. + match self.swap_state_map.get_mut(&action.transfer_id) { + Some(state) => { + state.retry_on_error += 1; + if state.retry_on_error < 6 { + // Depending on the action cancel transfer + match action.kind { + TransferActionType::CompleteBridgeTransfer { .. } + | TransferActionType::AbortedReplay { .. } => { + //Complete failed. retry + let transfer_id = state.transfer_id; + let action_kind = state.transition_from_aborted(transfer_id); + let action = TransferAction { + transfer_id: state.transfer_id, + kind: action_kind, + }; + Some(action) + } + //Could never errors. + TransferActionType::CompletedRemoveState => None, + TransferActionType::NoAction => None, + } + } else { + tracing::warn!("Relayer transfer failed: {} because send complete Tx failed more than 5 times.", state.transfer_id); + None + } + } + None => { + tracing::warn!( + "Receive an error for action but no state found for id:{}", + action.transfer_id + ); + None + } + } + } +} diff --git a/protocol-units/bridge/service/tests/relayer.rs b/protocol-units/bridge/service/tests/relayer.rs new file mode 100644 index 000000000..e7f389bc7 --- /dev/null +++ b/protocol-units/bridge/service/tests/relayer.rs @@ -0,0 +1,318 @@ +use bridge_service::types::Amount; +use bridge_util::chains::bridge_contracts::BridgeContractError; +use bridge_util::chains::bridge_contracts::BridgeContractResult; +use bridge_util::chains::bridge_contracts::BridgeTransferCompletedDetails; +use bridge_util::chains::bridge_contracts::BridgeTransferInitiatedDetails; +use bridge_util::types::AddressError; +use bridge_util::types::BridgeAddress; +use bridge_util::types::Nonce; +use bridge_util::BridgeContractEvent; +use bridge_util::BridgeContractMonitoring; +use bridge_util::BridgeRelayerContract; +use bridge_util::BridgeTransferId; +use futures::SinkExt; +use futures::{ + channel::mpsc::{UnboundedReceiver, UnboundedSender}, + Stream, StreamExt, +}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::{pin::Pin, task::Poll}; +use tiny_keccak::{Hasher, Keccak}; +use tokio::sync::mpsc; +use tokio::sync::oneshot; + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct MockAddress(pub Vec); + +impl From for Vec { + fn from(address: MockAddress) -> Self { + address.0 + } +} + +impl From> for MockAddress { + fn from(address: BridgeAddress) -> Self { + address.0 + } +} + +impl std::ops::Deref for MockAddress { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom> for MockAddress { + type Error = AddressError; + + fn try_from(vec: Vec) -> Result { + Ok(MockAddress(vec)) + } +} + +fn calculate_bridge_transfer_id( + initiator: &[u8], + recipient: &[u8], + amount: Amount, + nonce: Nonce, +) -> BridgeTransferId { + let mut hasher = Keccak::v256(); + hasher.update(initiator); + hasher.update(recipient); + hasher.update(&amount.to_le_bytes()); + hasher.update(&nonce.0.to_le_bytes()); + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + BridgeTransferId(output) +} + +#[derive(Clone)] +pub struct RelayerMockClient { + sender: UnboundedSender>>, + complete_notifier: + tokio::sync::mpsc::Sender>>, + send_retry: Arc, +} + +impl RelayerMockClient { + pub fn build( + send_retry: usize, + sender: UnboundedSender>>, + ) -> (Self, tokio::sync::mpsc::Receiver>>) + { + let (notifier_sender, notifier_listener) = tokio::sync::mpsc::channel::< + BridgeContractResult>, + >(100); + ( + RelayerMockClient { + sender, + complete_notifier: notifier_sender, + send_retry: Arc::new(AtomicUsize::new(send_retry)), + }, + notifier_listener, + ) + } +} + +#[async_trait::async_trait] +impl BridgeRelayerContract for RelayerMockClient { + async fn complete_bridge_transfer( + &mut self, + bridge_transfer_id: BridgeTransferId, + initiator: BridgeAddress>, + recipient: BridgeAddress, + amount: Amount, + nonce: Nonce, + ) -> BridgeContractResult<()> { + //manage Tx send error simulation + let retry = self.send_retry.fetch_sub(1, Ordering::Acquire); + // if retry unwrap (> 5000) should be considered as 0. + if retry != 0 && retry < 5000 { + self.complete_notifier + .send(Err(BridgeContractError::OnChainError("Retry.".to_string()))) + .await + .unwrap(); + return Err(BridgeContractError::OnChainError("Send Tx failed, retry".to_string())); + } + + //verify the transfer Id + let calcuated_bridge_transfer_id = + calculate_bridge_transfer_id(&initiator.0, &recipient.0 .0, amount, nonce); + if bridge_transfer_id != calcuated_bridge_transfer_id { + self.complete_notifier + .send(Err(BridgeContractError::OnChainError("Bad transfer Id.".to_string()))) + .await + .unwrap(); + return Err(BridgeContractError::OnChainError( + "Transfer Id verification failed".to_string(), + )); + } + + let details: BridgeTransferCompletedDetails = BridgeTransferCompletedDetails { + bridge_transfer_id, + initiator: BridgeAddress(initiator.0), + recipient: BridgeAddress(recipient.0), + nonce, + amount, + }; + let event = BridgeContractEvent::Completed(details); + self.sender.send(Ok(event.clone())).await.unwrap(); + self.complete_notifier.send(Ok(event)).await.unwrap(); + + Ok(()) + } + async fn get_bridge_transfer_details_with_nonce( + &mut self, + nonce: Nonce, + ) -> BridgeContractResult>> { + todo!() + } + async fn is_bridge_transfer_completed( + &mut self, + bridge_transfer_id: BridgeTransferId, + ) -> BridgeContractResult { + todo!() + } +} + +pub struct MockMonitoring { + listener: UnboundedReceiver>>, +} + +impl MockMonitoring { + pub fn build( + listener: UnboundedReceiver>>, + mut health_check_rx: mpsc::Receiver>, + ) -> Self { + //Start Health check loop + tokio::spawn({ + async move { + loop { + //Check if there's a health check request + if let Some(tx) = health_check_rx.recv().await { + if let Err(err) = tx.send(true) { + tracing::warn!("Mock Heath check send on oneshot channel failed:{err}"); + } + } + } + } + }); + + MockMonitoring { listener } + } +} + +impl BridgeContractMonitoring for MockMonitoring { + type Address = MockAddress; +} + +impl Stream for MockMonitoring { + type Item = BridgeContractResult>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll> { + let this = self.get_mut(); + this.listener.poll_next_unpin(cx) + } +} + +async fn initiate_bridge_transfer( + initiator: MockAddress, + recipient: MockAddress, + amount: Amount, + nonce: Nonce, + sender: &mut UnboundedSender>>, +) -> BridgeTransferId { + let bridge_transfer_id = + calculate_bridge_transfer_id(&initiator.0, &recipient.0, amount, nonce); + let details = BridgeTransferInitiatedDetails { + bridge_transfer_id, + initiator: BridgeAddress(initiator.clone()), + recipient: BridgeAddress(recipient.clone().0), + nonce, + amount, + }; + let event = BridgeContractEvent::Initiated(details); + + sender.send(Ok(event)).await.unwrap(); + bridge_transfer_id +} + +#[tokio::test] +async fn test_relayer_logic() -> Result<(), anyhow::Error> { + use tracing_subscriber::EnvFilter; + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), + ) + .init(); + + let l1_initiator_address = MockAddress(vec![11]); + let l2_recipient_address = MockAddress(vec![22]); + + let (mut l1_sender, l1_listener) = futures::channel::mpsc::unbounded::< + BridgeContractResult>, + >(); + let (l1_health_tx, l1_health_rx) = tokio::sync::mpsc::channel(10); + let l1_monitor = MockMonitoring::build(l1_listener, l1_health_rx); + let (l2_sender, l2_listener) = futures::channel::mpsc::unbounded::< + BridgeContractResult>, + >(); + let (l2_relayer_client, mut l2_mock_notifier) = RelayerMockClient::build(2, l2_sender.clone()); + + let (_l2_health_tx, l2_health_rx) = tokio::sync::mpsc::channel(10); + let l2_monitor = MockMonitoring::build(l2_listener, l2_health_rx); + + // Start relay in L1-> L2 direction + let _ = tokio::spawn({ + async move { + bridge_service::relayer::run_relayer_one_direction( + "L1->L2", + l1_monitor, + l2_relayer_client, + l2_monitor, + ) + .await + } + }); + println!("ICI3"); + + // Test send Tx fail 2 time L1-> L2 + let l1_transfer_id = initiate_bridge_transfer( + l1_initiator_address.clone(), + l2_recipient_address.clone(), + Amount(11), + Nonce(12), + &mut l1_sender, + ) + .await; + + // Get retry event. + let event = tokio::time::timeout(std::time::Duration::from_secs(5), l2_mock_notifier.recv()) + .await + .expect("L2 commplete not call by the relayer."); + assert!(event.is_some()); + let event = event.unwrap(); + assert!(event.is_err()); + + let event = tokio::time::timeout(std::time::Duration::from_secs(15), l2_mock_notifier.recv()) + .await + .expect("L2 commplete not call by the relayer."); + assert!(event.is_some()); + let event = event.unwrap(); + assert!(event.is_err()); + //get ok transfer completed + let event = tokio::time::timeout(std::time::Duration::from_secs(15), l2_mock_notifier.recv()) + .await + .expect("L2 commplete not call by the relayer."); + assert!(event.is_some()); + let event = event.unwrap(); + assert!(event.is_ok()); + let event = event.unwrap(); + assert_eq!(event.bridge_transfer_id(), l1_transfer_id); + + // Test Happy path L1->L2 + // Simulate Initiate transfer event. + let l1_transfer_id = initiate_bridge_transfer( + l1_initiator_address, + l2_recipient_address, + Amount(111), + Nonce(112), + &mut l1_sender, + ) + .await; + // Wait for completed notification. + let event = tokio::time::timeout(std::time::Duration::from_secs(5), l2_mock_notifier.recv()) + .await + .expect("L2 commplete not call by the relayer."); + assert!(event.is_some()); + let event = event.unwrap(); + assert!(event.is_ok()); + let event = event.unwrap(); + assert_eq!(event.bridge_transfer_id(), l1_transfer_id); + + Ok(()) +} diff --git a/protocol-units/bridge/setup/Cargo.toml b/protocol-units/bridge/setup/Cargo.toml index fb8363c67..e1ccf7b88 100644 --- a/protocol-units/bridge/setup/Cargo.toml +++ b/protocol-units/bridge/setup/Cargo.toml @@ -18,6 +18,7 @@ dot-movement = { workspace = true } commander = { workspace = true } alloy = { workspace = true } alloy-primitives = { workspace = true } +ethabi = { workspace = true } anyhow = { workspace = true } k256 = { workspace = true } rand = { workspace = true } @@ -33,8 +34,6 @@ mcr-settlement-config = { workspace = true } aptos-sdk = { workspace = true } -ethabi = "18.0.0" - [lints] #workspace = true diff --git a/protocol-units/bridge/setup/src/deploy.rs b/protocol-units/bridge/setup/src/deploy.rs index 76762c1ec..b22d3802c 100644 --- a/protocol-units/bridge/setup/src/deploy.rs +++ b/protocol-units/bridge/setup/src/deploy.rs @@ -1,19 +1,12 @@ use alloy::{ network::EthereumWallet, providers::ProviderBuilder, signers::local::PrivateKeySigner, }; -use alloy_primitives::{Address, U256}; -use bridge_config::{ - common::{eth::EthConfig, movement::MovementConfig}, - Config as BridgeConfig, -}; -use bridge_service::{ - chains::ethereum::{ - types::{ - AtomicBridgeCounterpartyMOVE, AtomicBridgeInitiatorMOVE, EthAddress, MockMOVEToken, - }, - utils::{send_transaction, send_transaction_rules}, - }, - types::TimeLock, +use alloy_primitives::Address; +use alloy_primitives::U256; +use bridge_config::{common::movement::MovementConfig, Config as BridgeConfig}; +use bridge_service::chains::ethereum::{ + types::{EthAddress, MockMOVEToken, NativeBridgeContract}, + utils::{send_transaction, send_transaction_rules}, }; use hex::ToHex; use std::io::BufRead; @@ -51,36 +44,29 @@ pub async fn setup_local_ethereum(config: &mut BridgeConfig) -> Result<(), anyho let rpc_url = config.eth.eth_rpc_connection_url(); tracing::info!("Bridge deploy setup_local_ethereum"); - config.eth.eth_initiator_contract = deploy_eth_initiator_contract(config).await?.to_string(); + config.eth.eth_native_contract = deploy_eth_native_contract(config).await?.to_string(); tracing::info!("Bridge deploy after intiator"); tracing::info!("Signer private key: {:?}", signer_private_key.address()); - config.eth.eth_counterparty_contract = - deploy_counterpart_contract(signer_private_key.clone(), &rpc_url) - .await - .to_string(); let move_token_contract = deploy_move_token_contract(signer_private_key.clone(), &rpc_url).await; config.eth.eth_move_token_contract = move_token_contract.to_string(); - initialize_eth_contracts( + config.eth.eth_native_contract = initialize_eth_contracts( signer_private_key.clone(), &rpc_url, - &config.eth.eth_initiator_contract, - &config.eth.eth_counterparty_contract, + &config.eth.eth_native_contract, EthAddress(move_token_contract), EthAddress(signer_private_key.address()), - *TimeLock(config.eth.time_lock_secs), config.eth.gas_limit, config.eth.transaction_send_retries, ) - .await?; + .await? + .to_string(); Ok(()) } -async fn deploy_eth_initiator_contract( - config: &mut BridgeConfig, -) -> Result { +async fn deploy_eth_native_contract(config: &mut BridgeConfig) -> Result { let signer_private_key = config.eth.signer_private_key.parse::()?; println!("ICI {:?}", config.eth.eth_rpc_connection_url()); let rpc_url = config.eth.eth_rpc_connection_url(); @@ -92,30 +78,13 @@ async fn deploy_eth_initiator_contract( .await .expect("Error during provider creation"); - let contract = AtomicBridgeInitiatorMOVE::deploy(rpc_provider.clone()) + let contract = NativeBridgeContract::deploy(rpc_provider.clone()) .await .expect("Failed to deploy AtomicBridgeInitiatorMOVE"); tracing::info!("initiator_contract address: {}", contract.address().to_string()); Ok(contract.address().to_owned()) } -async fn deploy_counterpart_contract( - signer_private_key: PrivateKeySigner, - rpc_url: &str, -) -> Address { - let rpc_provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(EthereumWallet::from(signer_private_key)) - .on_builtin(rpc_url) - .await - .expect("Error during provider creation"); - let contract = AtomicBridgeCounterpartyMOVE::deploy(rpc_provider.clone()) - .await - .expect("Failed to deploy AtomicBridgeCounterpartyMOVE"); - tracing::info!("counterparty_contract address: {}", contract.address().to_string()); - contract.address().to_owned() -} - async fn deploy_move_token_contract( signer_private_key: PrivateKeySigner, rpc_url: &str, @@ -133,18 +102,19 @@ async fn deploy_move_token_contract( move_token.address().to_owned() } +use ethabi::{Contract, Token}; +use serde_json::{from_str, Value}; + async fn initialize_eth_contracts( signer_private_key: PrivateKeySigner, rpc_url: &str, - initiator_contract_address: &str, - counterpart_contract_address: &str, + native_bridge_contract_address: &str, move_token: EthAddress, owner: EthAddress, - timelock: u64, gas_limit: u64, transaction_send_retries: u32, -) -> Result<(), anyhow::Error> { - tracing::info!("Setup Eth initialize_initiator_contract with timelock:{timelock});"); +) -> Result { + tracing::info!("Setup Eth initialize_initiator_contract."); let signer_address = signer_private_key.address(); let rpc_provider = ProviderBuilder::new() @@ -153,6 +123,7 @@ async fn initialize_eth_contracts( .on_builtin(rpc_url) .await .expect("Error during provider creation"); + let native_bridge_contract_address: Address = native_bridge_contract_address.parse()?; // Initialize the MockMOVEToken contract with the initiator address as the initial fund recipient let mock_move_token = MockMOVEToken::new(*move_token, &rpc_provider); @@ -167,41 +138,59 @@ async fn initialize_eth_contracts( ) .await; - let initiator_contract = - AtomicBridgeInitiatorMOVE::new(initiator_contract_address.parse()?, rpc_provider.clone()); - - let call = initiator_contract - .initialize(move_token.0, owner.0, U256::from(timelock), U256::from(1000000)) - .from(owner.0); - send_transaction( - call, - signer_address, - &send_transaction_rules(), - transaction_send_retries, - gas_limit.into(), + // create proxy contracts. + let proxy_admin = ProxyAdmin::deploy(rpc_provider.clone(), signer_address) + .await + .expect("Failed to deploy ProxyAdmin"); + // Deploy TransparentUpgradeableProxy for AtomicBridgeCounterparty + + // Load the ABI from a JSON file or inline JSON + // let contract_abi = include_bytes!("../../service/abis/AtomicBridgeInitiator.json"); + let path = std::env::current_dir() + .unwrap() // unwrap ok current dir set during setup start. + .join("protocol-units/bridge/service/abis/NativeBridge.json"); + let data = std::fs::read_to_string(path).expect("Unable to read ABI file"); + + // Parse the JSON data + let v: Value = from_str(&data).expect("Unable to parse JSON"); + + // Extract the "abi" field + let abi = v["abi"].to_string(); + + let contract = Contract::load(abi.as_bytes()).expect("Incorrect ABI"); + let function = contract.function("initialize").expect("Function must exist in ABI"); + let tokens = vec![ + Token::Address(ethabi::Address::from_slice(move_token.as_slice())), + Token::Address(ethabi::Address::from_slice(signer_address.as_slice())), + Token::Address(ethabi::Address::from_slice(signer_address.as_slice())), + Token::Address(ethabi::Address::from_slice(Address::ZERO.as_slice())), + ]; + // Encode the function call + let initialize_data = function.encode_input(&tokens).unwrap(); + + let upgradeable_proxy = TransparentUpgradeableProxy::deploy( + rpc_provider.clone(), // The provider (same one used for deployment) + native_bridge_contract_address, // Address of the contract + *proxy_admin.address(), + initialize_data.clone().into(), ) - .await - .expect("Failed to send transaction"); - - let counterpart_contract = AtomicBridgeCounterpartyMOVE::new( - counterpart_contract_address.parse()?, - rpc_provider.clone(), - ); - - let call = counterpart_contract - .initialize(initiator_contract_address.parse()?, owner.0, U256::from(timelock)) - .from(owner.0); - send_transaction( - call, + .await?; + + // Transfer some coin to the native contract (using the proxy address) so that it can complete transfer. + let transfer_token_call = mock_move_token + .transfer(*upgradeable_proxy.address(), U256::from(10000000000u64) * U256::from(100000u64)) + .from(signer_address); + + let _ = send_transaction( + transfer_token_call, signer_address, &send_transaction_rules(), transaction_send_retries, gas_limit.into(), ) - .await - .expect("Failed to send transaction"); + .await; - Ok(()) + Ok(*upgradeable_proxy.address()) } pub fn deploy_local_movement_node(config: &mut MovementConfig) -> Result<(), anyhow::Error> { @@ -265,7 +254,7 @@ pub fn init_movement_node(config: &mut MovementConfig) -> Result<(), anyhow::Err Ok(()) } -pub fn deploy_on_movement_framework(config: &mut MovementConfig) -> Result<(), anyhow::Error> { +pub fn deploy_on_movement_framework(_config: &mut MovementConfig) -> Result<(), anyhow::Error> { tracing::info!("Before compile move modules"); let compile_output = Command::new("movement") .args(&["move", "compile", "--package-dir", "protocol-units/bridge/move-modules/"]) @@ -333,12 +322,12 @@ pub fn deploy_on_movement_framework(config: &mut MovementConfig) -> Result<(), a ); } - let update_bridge_operator_output = Command::new("movement") + let update_bridge_relayer_output = Command::new("movement") .args(&[ "move", "run-script", "--compiled-script-path", - "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/update_bridge_operator.mv", + "protocol-units/bridge/move-modules/build/bridge-modules/bytecode_scripts/update_bridge_relayer.mv", "--args", "address:0xf90391c81027f03cdea491ed8b36ffaced26b6df208a9b569e5baf2590eb9b16", "--profile", @@ -349,16 +338,16 @@ pub fn deploy_on_movement_framework(config: &mut MovementConfig) -> Result<(), a .stderr(Stdio::piped()) .output()?; - if !update_bridge_operator_output.stdout.is_empty() { + if !update_bridge_relayer_output.stdout.is_empty() { println!( - "run-script update_bridge_operatorstdout: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stdout) + "run-script update_bridge_relayer stdout: {}", + String::from_utf8_lossy(&update_bridge_relayer_output.stdout) ); } - if !update_bridge_operator_output.stderr.is_empty() { + if !update_bridge_relayer_output.stderr.is_empty() { eprintln!( - "run-script update_bridge_operator supdate_bridge_operator tderr: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stderr) + "run-script update_bridge_relayer update_bridge_relayer stderr: {}", + String::from_utf8_lossy(&update_bridge_relayer_output.stderr) ); } @@ -381,13 +370,13 @@ pub fn deploy_on_movement_framework(config: &mut MovementConfig) -> Result<(), a if !set_initiator_time_lock_script_output.stdout.is_empty() { println!( "run-script set_initiator_time_lock_duration stdout: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stdout) + String::from_utf8_lossy(&update_bridge_relayer_output.stdout) ); } if !set_initiator_time_lock_script_output.stderr.is_empty() { eprintln!( "run-script set_initiator_time_lock_duration stderr: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stderr) + String::from_utf8_lossy(&update_bridge_relayer_output.stderr) ); } @@ -410,13 +399,13 @@ pub fn deploy_on_movement_framework(config: &mut MovementConfig) -> Result<(), a if !set_counterparty_time_lock_script_output.stdout.is_empty() { println!( "run-script set_counterparty_time_lock_duration stdout: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stdout) + String::from_utf8_lossy(&update_bridge_relayer_output.stdout) ); } if !set_counterparty_time_lock_script_output.stderr.is_empty() { eprintln!( "run-script set_counterparty_time_lock_duration stderr: {}", - String::from_utf8_lossy(&update_bridge_operator_output.stderr) + String::from_utf8_lossy(&update_bridge_relayer_output.stderr) ); } diff --git a/protocol-units/bridge/setup/src/lib.rs b/protocol-units/bridge/setup/src/lib.rs index 2279b6143..3adcfe9e9 100644 --- a/protocol-units/bridge/setup/src/lib.rs +++ b/protocol-units/bridge/setup/src/lib.rs @@ -1,4 +1,3 @@ -use alloy::node_bindings::AnvilInstance; use bridge_config::Config; pub mod deploy; @@ -15,8 +14,6 @@ pub async fn process_compose_setup(config: Config) -> Result Result { //let anvil = local::setup_eth(&mut config.eth, &mut config.testing); - //Define the timelock to 15s for the test - config.eth.time_lock_secs = 15; //Deploy locally crate::deploy::setup_local_ethereum(&mut config).await?; Ok(config) diff --git a/protocol-units/bridge/setup/src/main.rs b/protocol-units/bridge/setup/src/main.rs index a93551ec7..3fe0c912a 100644 --- a/protocol-units/bridge/setup/src/main.rs +++ b/protocol-units/bridge/setup/src/main.rs @@ -96,9 +96,6 @@ async fn main() -> Result<(), anyhow::Error> { .clone(); } - //set timelock for e2e test - config.eth.time_lock_secs = 60; // 1mn for the e2e test. - // Use custom as movement node in init. config.movement.mvt_init_network = "custom".to_string(); diff --git a/protocol-units/bridge/state_logic.md b/protocol-units/bridge/state_logic.md index b20e3a2b3..1689c5cfd 100644 --- a/protocol-units/bridge/state_logic.md +++ b/protocol-units/bridge/state_logic.md @@ -1,67 +1,34 @@ # Bridge transfer state logic definition -## Normal transfer process state changes +## Transfer process state changes ```mermaid flowchart TB L1{"L1"} --Event BridgeTransferInitiated--> B("Initialized") - B --Action lock_bridge_transfer--> L2("L2") - L2 --Event BridgeTransferLockedEvent--> C("Locked") - L2 --Event BridgeTransferCompletedEvent--> D("KnowSecret") - D -- Action BridgeTransferCompletedEvent --> L1 - L1 -- Event BridgeTransferCompleted --> E("Done(Completed)") - L2 -- Event lock_bridge_transfer Failed --> F("NeedRefund") + B --Action complete_bridge_transfer--> L2("L2") + L2 --Event BridgeTransferCompletedEvent--> C("Completed") + L2 -- Event BridgeTransferCompleted Tx Failed --> D("Aborted") + D --Action complete_bridge_transfer--> L2("L2") ``` -## State changes with error cases - -```mermaid -flowchart TB - L1{"L1"} --Event BridgeTransferInitiated--> B("Initialized") - B --Action lock_bridge_transfer--> L2("L2") - L2 --Event BridgeTransferLockedEvent--> C("Locked") - L2 --Event BridgeTransferCompletedEvent--> D("KnowSecret") - D --Action completeBridgeTransfer--> L1 - L1 --Event BridgeTransferCompleted--> G1("Done(Completed)") - - L2 --"Action lock_bridge_transfer Failed"--> F("NeedRefund") - F --"Action refundBridgeTransfer"--> L1 - L1 --"Event BridgeTransferRefunded"--> G2("Done(Refunded)") - L2 --"Action complete_bridge_transfer Failed"--> F - L1 --"Event completeBridgeTransfer Failed"--> D - L1 --"Action refundBridgeTransfer Failed"--> F - L1 -- Action completeBridgeTransfer Failed --> D - - L2 --"Event BridgeTransferCancelledEvent"--> F - -``` ## Event / Actions of all states ```mermaid flowchart TB - + BE{"Event BridgeTransferInitiated"}-->BB("Initialized") - BB -->BA{"Action lock_bridge_transfer"} + BB -->BA{"Action completeBridgeTransfer"} - CE{"Event BridgeTransferLockedEvent"}-->CC("Locked") - CC -->CA("Action None") + CE{"Event BridgeTransferCompletedEvent"}-->CC("Completed") + CC -->CA("Action Remove state") - DE{"Event BridgeTransferCompletedEvent"}-->DD("KnowSecret") - DD -->DA("Action completeBridgeTransfer") - DDE{"Event L1 completeBridgeTransfer Failed"}-->DD - - EE{"Event BridgeTransferCompleted"}-->EEE("Done(Completed)") - EEE -->EA("Action Done") - - - FE{"Event lock_bridge_transfer Failed"}-->FF(""Need Refund"") - FF -->FA("Action refundBridgeTransfer") - - GE{"Event L2 completeBridgeTransfer Failed"}-->FF - HE{"Event refundBridgeTransfer Failed"}-->FF - IE{"Event BridgeTransferCancelledEvent Failed"}-->FF + DE{"Event BridgeTransferCompleted Tx fail"}-->DD("Aborted") + DD -->DA("Action wait -> completeBridgeTransfer") + EE{"Event BridgeTransferCompleted Event fail to read"}-->EEE("Error No change") + EEE -->EA("Action Log error") + FE{"Event BridgeTransferInitiated Event fail to read"}-->EEE("Error No change") ``` \ No newline at end of file diff --git a/protocol-units/bridge/util/Cargo.toml b/protocol-units/bridge/util/Cargo.toml index cc8e605fa..7cba36e75 100644 --- a/protocol-units/bridge/util/Cargo.toml +++ b/protocol-units/bridge/util/Cargo.toml @@ -11,10 +11,14 @@ rust-version.workspace = true [dependencies] thiserror = { workspace = true } -tokio-stream = { workspace = true } async-trait = { workspace = true } rand = { workspace = true } serde = { workspace = true } hex = { workspace = true } derive_more = { workspace = true } -alloy = { workspace = true, features = ["serde"]} \ No newline at end of file +alloy = { workspace = true, features = ["serde"]} +tokio = { workspace = true, features = ["full"] } +tokio-stream = { workspace = true } +tracing.workspace = true +futures.workspace = true +anyhow = { workspace = true } diff --git a/protocol-units/bridge/util/src/actions.rs b/protocol-units/bridge/util/src/actions.rs index ba0c98adb..5cf5f6522 100644 --- a/protocol-units/bridge/util/src/actions.rs +++ b/protocol-units/bridge/util/src/actions.rs @@ -1,6 +1,5 @@ use crate::chains::bridge_contracts::BridgeContractError; -use crate::types::ChainId; -use crate::types::{Amount, BridgeAddress, BridgeTransferId, HashLock, HashLockPreImage}; +use crate::types::{Amount, BridgeAddress, BridgeTransferId, Nonce}; use std::fmt; use thiserror::Error; @@ -21,38 +20,42 @@ impl fmt::Display for ActionExecError { #[derive(Debug, Clone)] pub struct TransferAction { - pub chain: ChainId, pub transfer_id: BridgeTransferId, pub kind: TransferActionType, } impl fmt::Display for TransferAction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Action: {}/{}/{}", self.chain, self.transfer_id, self.kind) + write!(f, "Action: {} / {}", self.transfer_id, self.kind) } } #[derive(Debug, Clone)] pub enum TransferActionType { - LockBridgeTransfer { + CompleteBridgeTransfer { bridge_transfer_id: BridgeTransferId, - hash_lock: HashLock, initiator: BridgeAddress>, recipient: BridgeAddress>, amount: Amount, + nonce: Nonce, + }, + CompletedRemoveState, + AbortedReplay { + bridge_transfer_id: BridgeTransferId, + initiator: BridgeAddress>, + recipient: BridgeAddress>, + amount: Amount, + nonce: Nonce, + wait_time_sec: u64, }, - WaitAndCompleteInitiator(u64, HashLockPreImage), - RefundInitiator, - TransferDone, NoAction, } impl fmt::Display for TransferActionType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let act = match self { - TransferActionType::LockBridgeTransfer { .. } => "LockBridgeTransfer", - TransferActionType::WaitAndCompleteInitiator(..) => "WaitAndCompleteInitiator", - TransferActionType::RefundInitiator => "RefundInitiator", - TransferActionType::TransferDone => "TransferDone", + TransferActionType::CompleteBridgeTransfer { .. } => "CompleteBridgeTransfer", + TransferActionType::CompletedRemoveState => "CompletedRemoveState", + TransferActionType::AbortedReplay { .. } => "AbortedReplay", TransferActionType::NoAction => "NoAction", }; write!(f, "{}", act) diff --git a/protocol-units/bridge/util/src/chains/bridge_contracts.rs b/protocol-units/bridge/util/src/chains/bridge_contracts.rs index b1bbfee4e..270b1cbb2 100644 --- a/protocol-units/bridge/util/src/chains/bridge_contracts.rs +++ b/protocol-units/bridge/util/src/chains/bridge_contracts.rs @@ -1,12 +1,9 @@ -use crate::types::{BridgeTransferDetailsCounterparty, LockDetails}; +use crate::types::{Amount, BridgeAddress, BridgeTransferId, Nonce}; +use serde::Deserialize; use std::fmt; use thiserror::Error; use tokio_stream::Stream; -use crate::types::{ - Amount, BridgeAddress, BridgeTransferDetails, BridgeTransferId, HashLock, HashLockPreImage, -}; - #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum BridgeContractError { #[error("Account balance error")] @@ -88,32 +85,20 @@ pub type BridgeContractWETH9Result = Result; #[derive(Clone, Debug, PartialEq, Eq)] pub enum BridgeContractEventType { Initiated, - Locked, - InitiatorCompleted, - CounterPartyCompleted, - Cancelled, - Refunded, + Completed, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum BridgeContractEvent { - Initiated(BridgeTransferDetails), - Locked(LockDetails), - InitiatorCompleted(BridgeTransferId), - CounterPartyCompleted(BridgeTransferId, HashLockPreImage), - Cancelled(BridgeTransferId), - Refunded(BridgeTransferId), + Initiated(BridgeTransferInitiatedDetails), + Completed(BridgeTransferCompletedDetails), } impl BridgeContractEvent { pub fn bridge_transfer_id(&self) -> BridgeTransferId { match self { Self::Initiated(details) => details.bridge_transfer_id, - Self::Locked(details) => details.bridge_transfer_id, - Self::InitiatorCompleted(id) - | Self::CounterPartyCompleted(id, _) - | Self::Cancelled(id) - | Self::Refunded(id) => *id, + Self::Completed(details) => details.bridge_transfer_id, } } @@ -130,16 +115,30 @@ impl fmt::Display for BridgeContractEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let kind = match self { Self::Initiated(_) => "Initiated", - Self::Locked(_) => "Locked", - Self::InitiatorCompleted(_) => "InitiatorCompleted", - Self::CounterPartyCompleted(_, _) => "CounterPartyCompleted", - Self::Cancelled(_) => "Cancelled", - Self::Refunded(_) => "Refunded", + Self::Completed(_) => "Completed", }; write!(f, "Contract event: {}/ transfer id: {}", kind, self.bridge_transfer_id(),) } } +#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] +pub struct BridgeTransferInitiatedDetails { + pub bridge_transfer_id: BridgeTransferId, + pub initiator: BridgeAddress, + pub recipient: BridgeAddress>, + pub amount: Amount, + pub nonce: Nonce, +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] +pub struct BridgeTransferCompletedDetails { + pub bridge_transfer_id: BridgeTransferId, + pub initiator: BridgeAddress>, + pub recipient: BridgeAddress, + pub amount: Amount, + pub nonce: Nonce, +} + pub trait BridgeContractMonitoring: Stream>> + Unpin { @@ -147,55 +146,38 @@ pub trait BridgeContractMonitoring: } #[async_trait::async_trait] -pub trait BridgeContract: Clone + Unpin + Send + Sync { +pub trait BridgeClientContract: Clone + Unpin + Send + Sync { async fn initiate_bridge_transfer( &mut self, - initiator: BridgeAddress, recipient: BridgeAddress>, - hash_lock: HashLock, amount: Amount, ) -> BridgeContractResult<()>; - - async fn initiator_complete_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - secret: HashLockPreImage, - ) -> BridgeContractResult<()>; - - async fn counterparty_complete_bridge_transfer( - &mut self, - bridge_transfer_id: BridgeTransferId, - secret: HashLockPreImage, - ) -> BridgeContractResult<()>; - - async fn refund_bridge_transfer( + async fn get_bridge_transfer_details( &mut self, bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()>; - - async fn get_bridge_transfer_details_initiator( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>>; - - async fn get_bridge_transfer_details_counterparty( - &mut self, - bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult>>; + ) -> BridgeContractResult>>; +} - async fn lock_bridge_transfer( +#[async_trait::async_trait] +pub trait BridgeRelayerContract: Clone + Unpin + Send + Sync { + async fn complete_bridge_transfer( &mut self, bridge_transfer_id: BridgeTransferId, - hash_lock: HashLock, initiator: BridgeAddress>, recipient: BridgeAddress, amount: Amount, + nonce: Nonce, ) -> BridgeContractResult<()>; - async fn abort_bridge_transfer( + async fn get_bridge_transfer_details_with_nonce( + &mut self, + nonce: Nonce, + ) -> BridgeContractResult>>; + + async fn is_bridge_transfer_completed( &mut self, bridge_transfer_id: BridgeTransferId, - ) -> BridgeContractResult<()>; + ) -> BridgeContractResult; } #[async_trait::async_trait] diff --git a/protocol-units/bridge/util/src/chains/mod.rs b/protocol-units/bridge/util/src/chains/mod.rs index 0f19c1a4d..1bfcc1b22 100644 --- a/protocol-units/bridge/util/src/chains/mod.rs +++ b/protocol-units/bridge/util/src/chains/mod.rs @@ -1 +1,81 @@ +//use bridge_indexer_db::client::Client as IndexerClient; +use futures::stream::FuturesUnordered; +use tokio::select; +use tokio::sync::mpsc; +use tokio::sync::oneshot; +use tokio_stream::StreamExt; + pub mod bridge_contracts; + +pub async fn check_monitoring_health( + chain: &str, + healthcheck_source: mpsc::Sender>, + mut healthcheck_request_rx: mpsc::Receiver>, +) -> Result<(), anyhow::Error> { + let mut monitoring_health_check_interval = + tokio::time::interval(tokio::time::Duration::from_secs(5)); + let mut health_status = true; // init health check with alive status. + let mut health_check_result_futures = FuturesUnordered::new(); + + loop { + select! { + // Health Check routine. + // Manage REST HealthCheck request + Some(oneshot_tx) = healthcheck_request_rx.recv() => { + if let Err(err) = oneshot_tx.send(health_status){ + tracing::warn!("Heal check {chain} oneshot channel closed abnormally :{err:?}"); + } + + } + // Verify that monitoring heath check still works. + _ = monitoring_health_check_interval.tick() => { + //Chain source monitoring health check. + let jh = tokio::spawn({ + let healthcheck_tx = healthcheck_source.clone(); + async move { + check_monitoring_loop_heath(healthcheck_tx).await + } + }); + health_check_result_futures.push(jh); + } + // Process health check result. + Some(res) = health_check_result_futures.next() => { + match res { + //Client execution ok. + Ok(Ok(status)) => health_status = status, + Ok(Err(err)) => { + tracing::warn!("Health monitor:{chain} monitoring health check fail with an error:{err}",); + health_status = false; + }, + Err(err)=>{ + // Tokio execution fail. Process should exit. + tracing::error!("Health monitor:{chain} , Error during health check tokio task execution exiting: {err}"); + return Err(err.into()); + } + } + } + } + } +} + +async fn check_monitoring_loop_heath( + healthcheck_tx: mpsc::Sender>, +) -> Result { + let (tx, rx) = oneshot::channel(); + healthcheck_tx + .send(tx) + .await + .map_err(|err| format!("Health check send error: {}", err))?; + let res = match tokio::time::timeout(tokio::time::Duration::from_secs(5), rx).await { + Ok(Ok(res)) => res, + Ok(Err(err)) => { + tracing::warn!("Monitoring health check return an error:{err}"); + false + } + Err(_) => { + tracing::warn!("Monitoring health check timeout. Monitoring is idle."); + false + } + }; + Ok(res) +} diff --git a/protocol-units/bridge/util/src/events.rs b/protocol-units/bridge/util/src/events.rs index e898eafdd..7030cc8c9 100644 --- a/protocol-units/bridge/util/src/events.rs +++ b/protocol-units/bridge/util/src/events.rs @@ -1,5 +1,4 @@ use crate::chains::bridge_contracts::BridgeContractEvent; -use crate::types::ChainId; use std::fmt; use thiserror::Error; @@ -19,13 +18,12 @@ pub enum InvalidEventError { #[derive(Clone, Debug, PartialEq, Eq)] pub struct TransferEvent { - pub chain: ChainId, pub contract_event: BridgeContractEvent, } -impl From<(BridgeContractEvent, ChainId)> for TransferEvent { - fn from((event, chain): (BridgeContractEvent, ChainId)) -> Self { - TransferEvent { chain, contract_event: event } +impl From> for TransferEvent { + fn from(event: BridgeContractEvent) -> Self { + TransferEvent { contract_event: event } } } @@ -33,42 +31,9 @@ impl fmt::Display for TransferEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Event: {}: {} => {:?}", - self.chain, + "Event: {} => {:?}", self.contract_event.bridge_transfer_id(), self.contract_event, ) } } - -// impl -// From<( -// BridgeContractEvent< -// >, -// > as BridgeContractMonitoring>::Address, -// >, -// ChainId, -// )> -// for TransferEvent< -// >, -// > as BridgeContractMonitoring>::Address, -// > -// { -// fn from( -// (event, chain): ( -// BridgeContractEvent< -// >, -// > as BridgeContractMonitoring>::Address, -// >, -// ChainId, -// ), -// ) -> Self { -// TransferEvent { chain, contract_event: event } -// } -// } diff --git a/protocol-units/bridge/util/src/lib.rs b/protocol-units/bridge/util/src/lib.rs index 0b1455011..acf020dd8 100644 --- a/protocol-units/bridge/util/src/lib.rs +++ b/protocol-units/bridge/util/src/lib.rs @@ -7,12 +7,12 @@ pub mod types; pub use crate::actions::ActionExecError; pub use crate::actions::TransferAction; pub use crate::actions::TransferActionType; -pub use crate::chains::bridge_contracts::BridgeContract; +pub use crate::chains::bridge_contracts::BridgeClientContract; pub use crate::chains::bridge_contracts::BridgeContractEvent; pub use crate::chains::bridge_contracts::BridgeContractMonitoring; +pub use crate::chains::bridge_contracts::BridgeRelayerContract; pub use crate::events::InvalidEventError; pub use crate::events::TransferEvent; pub use crate::states::TransferState; pub use crate::states::TransferStateType; pub use crate::types::BridgeTransferId; -pub use crate::types::ChainId; diff --git a/protocol-units/bridge/util/src/states.rs b/protocol-units/bridge/util/src/states.rs index 8cd7c42c9..8222b4191 100644 --- a/protocol-units/bridge/util/src/states.rs +++ b/protocol-units/bridge/util/src/states.rs @@ -1,11 +1,10 @@ use crate::chains::bridge_contracts::BridgeContractEvent; +use crate::chains::bridge_contracts::BridgeTransferInitiatedDetails; use crate::events::{InvalidEventError, TransferEvent}; use crate::types::Amount; use crate::types::BridgeAddress; -use crate::types::BridgeTransferDetails; -use crate::types::HashLockPreImage; -use crate::types::LockDetails; -use crate::types::{BridgeTransferId, ChainId, HashLock, TimeLock}; +use crate::types::BridgeTransferId; +use crate::types::Nonce; use crate::TransferAction; use crate::TransferActionType; use std::fmt; @@ -29,22 +28,14 @@ impl>> From for BridgeAddress { #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] pub enum TransferStateType { Initialized, - Locked, - SecretReceived, - CompletedIntiator, - Done, - Refund, + Completed, } impl fmt::Display for TransferStateType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let kind = match self { Self::Initialized => "Initialized", - Self::Locked => "Locked", - Self::SecretReceived => "SecretReceived", - Self::CompletedIntiator => "CompletedIntiator", - Self::Done => "Done", - Self::Refund => "Refund", + Self::Completed => "Completed", }; write!(f, "{}", kind,) } @@ -53,25 +44,18 @@ impl fmt::Display for TransferStateType { #[allow(dead_code)] pub struct TransferState { pub state: TransferStateType, - pub init_chain: ChainId, pub transfer_id: BridgeTransferId, - pub intiator_address: TransferAddress, - pub counter_part_address: TransferAddress, - pub hash_lock: HashLock, - pub time_lock: TimeLock, + pub initiator: TransferAddress, + pub recipient: TransferAddress, pub amount: Amount, - pub contract_state: u8, + pub nonce: Nonce, //Max number time action are retry for the whole transfer. pub retry_on_error: usize, } impl fmt::Display for TransferState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Transfer State: {} / transfer id: {} / init_chain: {} ", - self.state, self.transfer_id, self.init_chain - ) + write!(f, "Transfer State: {} / transfer id: {}", self.state, self.transfer_id) } } @@ -85,114 +69,56 @@ impl TransferState { // already present invalid Err(InvalidEventError::InitAnAlreadyExist) } - // Lock event must on on the counter part chain. - (BridgeContractEvent::Locked(_), TransferStateType::Initialized) => (event.chain - != self.init_chain) - .then_some(()) - .ok_or(InvalidEventError::BadChain), - // Lock event is only applied on Initialized swap state - (BridgeContractEvent::Locked(details), _) => Err(InvalidEventError::BadEvent(format!("Received a locked event with state not Initialized, transfer_id: {} state:{} details: {details:?}", self.transfer_id, self.state))), - // CounterPartCompleted event must on on the counter part chain. - (BridgeContractEvent::CounterPartyCompleted(_, _), TransferStateType::Locked) => { - (event.chain != self.init_chain) - .then_some(()) - .ok_or(InvalidEventError::BadChain) - } - // CounterPartCompleted event is only applied on Locked swap state - (BridgeContractEvent::CounterPartyCompleted(_, _), _) => { - Err(InvalidEventError::BadEvent(format!("Received a CounterPartCompleted event with state not Locked, transfer_id: {} state:{}", self.transfer_id, self.state))) - } - // InitiatorCompleted event must on on the init chain. - (BridgeContractEvent::InitiatorCompleted(_), TransferStateType::SecretReceived) => { - (event.chain == self.init_chain) - .then_some(()) - .ok_or(InvalidEventError::BadChain) - } - (BridgeContractEvent::InitiatorCompleted(_), _) => Err(InvalidEventError::BadEvent(format!("Received a InitialtorCompleted event with state not SecretReceived, transfer_id: {} state:{}", self.transfer_id, self.state))), - (BridgeContractEvent::Refunded(_), _) => Ok(()), - (&BridgeContractEvent::Cancelled(_), _) => Ok(()), + // Complete event must on on the counter part chain. + (BridgeContractEvent::Completed(_), _) => Ok(()), } } pub fn transition_from_initiated> + Clone>( - chain_id: ChainId, transfer_id: BridgeTransferId, - detail: BridgeTransferDetails, + detail: BridgeTransferInitiatedDetails, ) -> (Self, TransferAction) { - println!("State transition_from_initiated amount {:?}", detail.amount); - let state = TransferState { state: TransferStateType::Initialized, - init_chain: chain_id, transfer_id, - intiator_address: detail.initiator.clone().into(), - counter_part_address: detail.recipient.clone().into(), - hash_lock: detail.hash_lock, - time_lock: detail.time_lock, + initiator: detail.initiator.clone().into(), + recipient: detail.recipient.clone().into(), amount: detail.amount, - contract_state: detail.state, + nonce: detail.nonce, retry_on_error: 0, }; - let action_type = TransferActionType::LockBridgeTransfer { + let action_type = TransferActionType::CompleteBridgeTransfer { bridge_transfer_id: transfer_id, - hash_lock: detail.hash_lock, initiator: BridgeAddress(detail.initiator.0.into()), recipient: BridgeAddress(detail.recipient.0.into()), amount: detail.amount, + nonce: detail.nonce, }; - let action = TransferAction { chain: chain_id, transfer_id, kind: action_type }; + let action = TransferAction { transfer_id, kind: action_type }; (state, action) } - pub fn transition_from_locked_done> + Clone>( + pub fn transition_from_completed( mut self, _transfer_id: BridgeTransferId, - _detail: LockDetails, ) -> (Self, TransferActionType) { - self.state = TransferStateType::Locked; - let action_type = TransferActionType::NoAction; + self.state = TransferStateType::Completed; + let action_type = TransferActionType::CompletedRemoveState; (self, action_type) } - pub fn transition_from_counterpart_completed( - mut self, - _transfer_id: BridgeTransferId, - secret: HashLockPreImage, - ) -> (Self, TransferActionType) { - self.state = TransferStateType::SecretReceived; - let action_type = TransferActionType::WaitAndCompleteInitiator(0, secret); - (self, action_type) - } - - pub fn transition_from_initiator_completed( - mut self, - _transfer_id: BridgeTransferId, - ) -> (Self, TransferActionType) { - self.state = TransferStateType::Done; - let action_type = TransferActionType::NoAction; - (self, action_type) - } - - pub fn transition_from_cancelled( - mut self, - _transfer_id: BridgeTransferId, - ) -> (Self, TransferActionType) { - self.state = TransferStateType::Done; - let action_type = TransferActionType::NoAction; - (self, action_type) - } - - pub fn transition_from_refunded( - mut self, - _transfer_id: BridgeTransferId, - ) -> (Self, TransferActionType) { - self.state = TransferStateType::Done; - let action_type = TransferActionType::NoAction; - (self, action_type) - } + pub fn transition_from_aborted(&mut self, transfer_id: BridgeTransferId) -> TransferActionType { + self.state = TransferStateType::Initialized; + let action_type = TransferActionType::AbortedReplay { + bridge_transfer_id: transfer_id, + initiator: BridgeAddress(self.initiator.0.clone().into()), + recipient: BridgeAddress(self.recipient.0.clone().into()), + amount: self.amount, + nonce: self.nonce, + wait_time_sec: 10, //TODO set wait time in config. + }; - pub fn transition_to_refund(&self) -> (TransferStateType, TransferActionType) { - (TransferStateType::Refund, TransferActionType::RefundInitiator) + action_type } } diff --git a/protocol-units/bridge/util/src/types.rs b/protocol-units/bridge/util/src/types.rs index 9e43a7bb4..9ac5c062f 100644 --- a/protocol-units/bridge/util/src/types.rs +++ b/protocol-units/bridge/util/src/types.rs @@ -1,5 +1,4 @@ use alloy::primitives::Uint; -use alloy::serde::quantity::vec; use derive_more::{Deref, DerefMut}; use hex::{self, FromHexError}; use rand::Rng; @@ -23,30 +22,10 @@ pub enum AddressError { AddressConvertionlError(String), } -#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub enum ChainId { - ONE, - TWO, -} - -impl ChainId { - pub fn other(&self) -> ChainId { - match self { - ChainId::ONE => ChainId::TWO, - ChainId::TWO => ChainId::ONE, - } - } -} - -impl fmt::Display for ChainId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - ChainId::ONE => "ONE", - ChainId::TWO => "TWO", - }; - write!(f, "{}", s) - } -} +#[derive( + Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Deserialize, serde::Serialize, +)] +pub struct Nonce(pub u128); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] pub struct BridgeTransferId(pub BridgeHash); @@ -80,7 +59,7 @@ impl TryFrom> for BridgeTransferId { impl fmt::Display for BridgeTransferId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Bid: {}", hex::encode(self.0)) + write!(f, "{}", hex::encode(self.0)) } } @@ -127,61 +106,7 @@ where } } -#[derive(Deref, Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] -pub struct HashLock(pub [u8; 32]); - -impl HashLock { - pub fn parse(s: &str) -> Result { - let bytes = hex::decode(s)?; - let array: [u8; 32] = - bytes.as_slice().try_into().map_err(|_| FromHexError::InvalidStringLength)?; - Ok(HashLock(array)) - } - /// Generate a cryptographically secure random secret - pub fn random() -> Self { - let mut rng = rand::thread_rng(); - let mut secret = [0u8; 32]; - rng.fill(&mut secret); - HashLock(secret) - } - - pub fn test() -> Self { - let array = [0u8; 32]; - HashLock(array) - } -} - -#[derive(Deref, Debug, Clone, Copy, PartialEq, Eq, Deserialize)] -pub struct HashLockPreImage(pub [u8; 32]); - -impl AsRef<[u8]> for HashLockPreImage { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl HashLockPreImage { - /// Generate a cryptographically secure random secret - pub fn random() -> Self { - let mut rng = rand::thread_rng(); - let mut secret = [0u8; 32]; - rng.fill(&mut secret); - HashLockPreImage(secret) - } -} - -#[derive(Deref, Debug, Clone, Copy, PartialEq, Eq, Deserialize)] -pub struct TimeLock(pub u64); - -impl From> for TimeLock { - fn from(value: Uint<256, 4>) -> Self { - // Extract the lower 64 bits. - let lower_64_bits = value.as_limbs()[0]; - TimeLock(lower_64_bits) - } -} - -#[derive(Deref, DerefMut, Debug, Clone, Copy, PartialEq, Eq, Deserialize)] +#[derive(Deref, DerefMut, Debug, Clone, Copy, PartialEq, Eq, Deserialize, serde::Serialize)] pub struct Amount(pub u64); impl From> for Amount { @@ -197,35 +122,3 @@ pub enum ConversionError { #[error("Invalid conversion from AssetType to Uint")] InvalidConversion, } - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] -pub struct BridgeTransferDetails { - pub bridge_transfer_id: BridgeTransferId, - pub initiator: BridgeAddress, - pub recipient: BridgeAddress>, - pub hash_lock: HashLock, - pub time_lock: TimeLock, - pub amount: Amount, - pub state: u8, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] -pub struct BridgeTransferDetailsCounterparty { - pub bridge_transfer_id: BridgeTransferId, - pub initiator: BridgeAddress>, - pub recipient: BridgeAddress, - pub hash_lock: HashLock, - pub time_lock: TimeLock, - pub amount: Amount, - pub state: u8, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] -pub struct LockDetails { - pub bridge_transfer_id: BridgeTransferId, - pub initiator: BridgeAddress>, - pub recipient: BridgeAddress, - pub hash_lock: HashLock, - pub time_lock: TimeLock, - pub amount: Amount, -}