diff --git a/.github/workflows/ci-contracts.yml b/.github/workflows/ci-contracts.yml index 5a8c899..e8489c3 100644 --- a/.github/workflows/ci-contracts.yml +++ b/.github/workflows/ci-contracts.yml @@ -24,9 +24,22 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 - uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0 + - name: Reject changes to removed legacy contracts + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + changed_files="$(git diff --name-only --diff-filter=d "$BASE_SHA" "$HEAD_SHA" -- contracts/)" + if [[ -n "$changed_files" ]]; then + printf 'Changes to the removed contracts/ tree are not accepted:\n%s\n' "$changed_files" >&2 + exit 1 + fi + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | diff --git a/.github/workflows/ci-error-docs.yml b/.github/workflows/ci-error-docs.yml index d8310d2..d1c06b6 100644 --- a/.github/workflows/ci-error-docs.yml +++ b/.github/workflows/ci-error-docs.yml @@ -4,7 +4,6 @@ on: pull_request: branches: [main] paths: - - 'contracts/**' - 'COMEBACKHERE-contracts/**' - 'docs/error-codes.md' - 'scripts/check_error_docs_sync.sh' diff --git a/.github/workflows/ci-legacy-contracts.yml b/.github/workflows/ci-legacy-contracts.yml deleted file mode 100644 index 7a47716..0000000 --- a/.github/workflows/ci-legacy-contracts.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: CI - Legacy Contracts - -# Test the legacy contracts/ workspace (issue #751). -# This ensures that the in-tree contracts/ copy doesn't break even though -# development now targets COMEBACKHERE-contracts/. - -on: - pull_request: - branches: [main] - paths: - - 'contracts/**' - workflow_call: # called by ci-summary.yml - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: cargo test (legacy) - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0 - - - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('contracts/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Run tests for legacy contracts - run: cargo test --manifest-path contracts/Cargo.toml diff --git a/.github/workflows/ci-summary.yml b/.github/workflows/ci-summary.yml index 191a607..39b349e 100644 --- a/.github/workflows/ci-summary.yml +++ b/.github/workflows/ci-summary.yml @@ -30,10 +30,6 @@ jobs: name: ci / contract tests uses: ./.github/workflows/ci-contracts.yml - ci-legacy-contracts: - name: ci / legacy contract tests - uses: ./.github/workflows/ci-legacy-contracts.yml - ci-legacy-frontend: name: ci / legacy frontend tests uses: ./.github/workflows/ci-legacy-frontend.yml @@ -88,7 +84,6 @@ jobs: needs: - ci-frontend - ci-contracts - - ci-legacy-contracts - ci-legacy-frontend - ci-abi-metadata - ci-abi-snapshots diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 06e031b..814a581 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -6,7 +6,7 @@ This repository sits at the centre of the COMEBACKHERE protocol and orchestrates - **Backend API** — Node/Express service that fronts the contracts. - **Frontend UI** — React/Vite app used by merchants, payers, and admins. -Each layer has **two source trees in this repository**, plus CI jobs that pull a third copy from upstream. This document explains which tree to target when contributing. +Contracts have one canonical source tree in this repository. The backend and frontend also retain older mirrored trees while their migrations continue. This document explains which tree to target when contributing. ## Canonical trees (where changes go) @@ -18,27 +18,23 @@ The active source of truth for each layer lives in a tree with the `COMEBACKHERE | Backend (Node) | `comebackhere-backend/` | Referenced by `.github/workflows/ci.yml` for `npm ci`, `tsc --noEmit`, `npm run lint`, `npm run build`. | | Frontend (React) | `comebackhere-frontend/` | Same as backend — the `ci.yml` `working-directory` points here. | -New feature work, bug fixes, and contract changes should target these three directories by default — they're the only ones with a dedicated, contract-specific CI workflow. +New feature work, bug fixes, and contract changes should target these three directories by default — they're the source trees covered by the full CI workflows. -## Mirrored trees (older in-tree copies) +## Remaining mirrored trees -The repository also contains a parallel set of directories without the prefix: +The repository retains older backend and frontend copies without the prefix: -- `contracts/` and `backend/` and `frontend/` +- `backend/` and `frontend/` -These are in-tree copies of the canonical sources at earlier points in time. They are kept because: +The former contract mirror was removed in PR #948. The remaining mirrors are in-tree copies of canonical backend and frontend sources at earlier points in time. They are kept because: -- Process docs reference them by path. `docs/error-codes.md` calls out `contracts/invoice/src/lib.rs` as the source of `InvoiceError`; that file still lives here. - They give reviewers a familiar path while the migration to the `COMEBACKHERE-*` trees completes. -These trees are valid PR targets. They have dedicated CI coverage: +The remaining mirrors are valid PR targets. They have dedicated CI coverage: -- `ci-legacy-contracts.yml` runs `cargo test --manifest-path contracts/Cargo.toml` on changes to `contracts/**`. - `ci-legacy-frontend.yml` runs `npm test` for `frontend/` on changes to `frontend/**`. -The legacy contract and frontend tests use the same toolchain and patterns as the canonical trees. - -If your change is brand-new work, target the `COMEBACKHERE-*` tree to inherit full CI coverage and ensure parity is maintained. If your change is intentionally narrow (a doc tweak, a small Rust fix to legacy contract files), the mirrored tree is fine and will still be tested. +If your change is brand-new work, target the `COMEBACKHERE-*` tree to inherit full CI coverage and ensure parity is maintained. The remaining backend and frontend mirrors are still available during their migrations. ## CI checkout behaviour @@ -71,7 +67,7 @@ When working inside this repository alone, the in-tree `COMEBACKHERE-contracts/` ## Invoice state machine -The invoice contract (`COMEBACKHERE-contracts/contracts/invoice/src/lib.rs`, mirrored at `contracts/invoice/src/lib.rs`) is the source of truth every other layer reads from: backend status displays, the indexer, and both frontend apps all ultimately derive their view of an invoice from this state machine. The diagram below shows every `InvoiceStatus` value and the function whose call transitions an invoice into it. Any transition not shown here is illegal and the calling function returns an `InvalidStateTransition` / `NotPending` style error (see [docs/error-codes.md](docs/error-codes.md) for the exact variant and code per contract). +The invoice contract (`COMEBACKHERE-contracts/contracts/invoice/src/lib.rs`) is the source of truth every other layer reads from: backend status displays, the indexer, and both frontend apps all ultimately derive their view of an invoice from this state machine. The diagram below shows every `InvoiceStatus` value and the function whose call transitions an invoice into it. Any transition not shown here is illegal and the calling function returns an `InvalidStateTransition` / `NotPending` style error (see [docs/error-codes.md](docs/error-codes.md) for the exact variant and code per contract). ```mermaid stateDiagram-v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 19bc973..d8c1155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Changes are organized by component: **Contract**, **Backend**, and **Frontend**. ### Contract +#### Removed + +- Removed the obsolete legacy contract tree; `COMEBACKHERE-contracts/` is the canonical source. Closes #948. + #### Added - Invoice contract with `create_invoice` supporting minimum amount validation. diff --git a/SECURITY.md b/SECURITY.md index e51e750..93d1b5a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -21,7 +21,7 @@ The following components are in scope for responsible disclosure: | Component | Repository / Location | | --- | --- | -| Soroban smart contracts | `COMEBACKHERE-contracts/` and `contracts/` | +| Soroban smart contracts | `COMEBACKHERE-contracts/` | | Backend API | `comebackhere-backend` (separate repo) | | Frontend application | `frontend/` and `comebackhere-frontend/` | | Deployment scripts | `scripts/` | diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock deleted file mode 100644 index 4bcd060..0000000 --- a/contracts/Cargo.lock +++ /dev/null @@ -1,2904 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "api-integration-tests" -version = "0.1.0" -dependencies = [ - "reqwest", - "serde", - "serde_json", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - -[[package]] -name = "bytes-lit" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" -dependencies = [ - "num-bigint", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "cc" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "comebackhere-invoice" -version = "1.0.0" -dependencies = [ - "soroban-sdk", -] - -[[package]] -name = "comebackhere-settlement" -version = "1.0.0" -dependencies = [ - "proptest", - "soroban-sdk", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crate-git-revision" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" -dependencies = [ - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" -dependencies = [ - "quote", - "syn 2.0.119", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.119", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.119", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core 0.23.0", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "serde_core", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "rand_core", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "escape-bytes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" - -[[package]] -name = "ethnum" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" - -[[package]] -name = "fastrand" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" - -[[package]] -name = "futures-io" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" - -[[package]] -name = "futures-sink" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" - -[[package]] -name = "futures-task" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" - -[[package]] -name = "futures-util" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" -dependencies = [ - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "r-efi 6.0.0", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "h2" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.14.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "libc" -version = "0.2.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "log" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl" -version = "0.10.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.119", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "num-traits", - "rand 0.9.5", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "ref-cast" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" -version = "0.23.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" -dependencies = [ - "base64 0.22.1", - "bs58", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.0", - "schemars 0.9.0", - "schemars 1.2.2", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" -dependencies = [ - "darling 0.23.0", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "soroban-builtin-sdk-macros" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f57a68ef8777e28e274de0f3a88ad9a5a41d9a2eb461b4dd800b086f0e83b80" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "soroban-env-common" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1c89463835fe6da996318156d39f424b4f167c725ec692e5a7a2d4e694b3d" -dependencies = [ - "arbitrary", - "crate-git-revision", - "ethnum", - "num-derive", - "num-traits", - "serde", - "soroban-env-macros", - "soroban-wasmi", - "static_assertions", - "stellar-xdr", - "wasmparser", -] - -[[package]] -name = "soroban-env-guest" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bfb2536811045d5cd0c656a324cbe9ce4467eb734c7946b74410d90dea5d0ce" -dependencies = [ - "soroban-env-common", - "static_assertions", -] - -[[package]] -name = "soroban-env-host" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7a32c28f281c423189f1298960194f0e0fc4eeb72378028171e556d8cd6160" -dependencies = [ - "backtrace", - "curve25519-dalek", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "generic-array", - "getrandom 0.2.17", - "hex-literal", - "hmac", - "k256", - "num-derive", - "num-integer", - "num-traits", - "p256", - "rand", - "rand_chacha", - "rand 0.8.7", - "rand_chacha 0.3.1", - "sec1", - "sha2", - "sha3", - "soroban-builtin-sdk-macros", - "soroban-env-common", - "soroban-wasmi", - "static_assertions", - "stellar-strkey", - "wasmparser", -] - -[[package]] -name = "soroban-env-macros" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242926fe5e0d922f12d3796cd7cd02dd824e5ef1caa088f45fce20b618309f64" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "serde", - "serde_json", - "stellar-xdr", - "syn 2.0.119", -] - -[[package]] -name = "soroban-ledger-snapshot" -version = "21.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6edf92749fd8399b417192d301c11f710b9cdce15789a3d157785ea971576fa" -dependencies = [ - "serde", - "serde_json", - "serde_with", - "soroban-env-common", - "soroban-env-host", - "thiserror", -] - -[[package]] -name = "soroban-sdk" -version = "21.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcdf04484af7cc731a7a48ad1d9f5f940370edeea84734434ceaf398a6b862e" -dependencies = [ - "arbitrary", - "bytes-lit", - "ctor", - "derive_arbitrary", - "ed25519-dalek", - "rand", - "rand 0.8.7", - "rustc_version", - "serde", - "serde_json", - "soroban-env-guest", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk-macros", - "stellar-strkey", -] - -[[package]] -name = "soroban-sdk-macros" -version = "21.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0974e413731aeff2443f2305b344578b3f1ffd18335a7ba0f0b5d2eb4e94c9ce" -dependencies = [ - "crate-git-revision", - "darling 0.20.11", - "itertools", - "proc-macro2", - "quote", - "rustc_version", - "sha2", - "soroban-env-common", - "soroban-spec", - "soroban-spec-rust", - "stellar-xdr", - "syn 2.0.119", -] - -[[package]] -name = "soroban-spec" -version = "21.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c70b20e68cae3ef700b8fa3ae29db1c6a294b311fba66918f90cb8f9fd0a1a" -dependencies = [ - "base64 0.13.1", - "stellar-xdr", - "thiserror", - "wasmparser", -] - -[[package]] -name = "soroban-spec-rust" -version = "21.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2dafbde981b141b191c6c036abc86097070ddd6eaaa33b273701449501e43d3" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "sha2", - "soroban-spec", - "stellar-xdr", - "syn 2.0.119", - "thiserror", -] - -[[package]] -name = "soroban-wasmi" -version = "0.31.1-soroban.20.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" -dependencies = [ - "smallvec", - "spin", - "wasmi_arena", - "wasmi_core", - "wasmparser-nostd", -] - -[[package]] -name = "spin" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stellar-strkey" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" -dependencies = [ - "base32", - "crate-git-revision", - "thiserror", -] - -[[package]] -name = "stellar-xdr" -version = "21.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50" -dependencies = [ - "arbitrary", - "base64 0.13.1", - "crate-git-revision", - "escape-bytes", - "hex", - "serde", - "serde_with", - "stellar-strkey", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.3", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "time" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "time-macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "libc", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.119", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasmi_arena" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" - -[[package]] -name = "wasmi_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - -[[package]] -name = "wasmparser" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" -dependencies = [ - "indexmap 2.14.0", - "semver", -] - -[[package]] -name = "wasmparser-nostd" -version = "0.100.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" -dependencies = [ - "indexmap-nostd", -] - -[[package]] -name = "web-sys" -version = "0.3.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml deleted file mode 100644 index 1f62612..0000000 --- a/contracts/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# ╔══════════════════════════════════════════════════════════════╗ -# ║ MIRRORED / DEPRECATED TREE ║ -# ║ This workspace is an older in-tree copy of the canonical ║ -# ║ COMEBACKHERE-contracts/ workspace. It exists only for ║ -# ║ backward-compatible doc references and review familiarity. ║ -# ║ ║ -# ║ → All new contract work MUST target COMEBACKHERE-contracts/ ║ -# ║ → See ARCHITECTURE.md for the full rationale ║ -# ╚══════════════════════════════════════════════════════════════╝ -[workspace] -members = [ - "invoice", - "settlement", - "api-integration-tests", - "settlement", -] -resolver = "2" diff --git a/contracts/README.md b/contracts/README.md deleted file mode 100644 index 6d1c2e2..0000000 --- a/contracts/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# `contracts/` - -This is the **legacy, mirrored** Rust contracts tree. It is a valid PR -target — it does not have its own CI workflow, and it is still referenced -from documentation such as [`docs/error-codes.md`](../docs/error-codes.md) -— but it is not the canonical source of truth. - -**[`COMEBACKHERE-contracts/`](../COMEBACKHERE-contracts/) is canonical for -CI.** New contract work should target that tree: it is the one built and -tested by `make test` and `.github/workflows/ci-contracts.yml` (and the -other `ci-*.yml` workflows), and it is the source ABI snapshots in -[`abis/`](../abis/) are generated from. - -See [`CONTRIBUTING.md`](../CONTRIBUTING.md#which-stack-do-i-work-on) for the -full rule on which tree to target, and -[`ARCHITECTURE.md`](../ARCHITECTURE.md) for why both trees exist and how -they're kept in sync. diff --git a/contracts/api-integration-tests/Cargo.toml b/contracts/api-integration-tests/Cargo.toml deleted file mode 100644 index f1b92bf..0000000 --- a/contracts/api-integration-tests/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "api-integration-tests" -version = "0.1.0" -edition = "2021" - -# Integration tests only; not a library or binary. - -[[test]] -name = "api" -path = "tests/api.rs" - -[dev-dependencies] -reqwest = { version = "0.12", features = ["json", "blocking"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" diff --git a/contracts/api-integration-tests/src/lib.rs b/contracts/api-integration-tests/src/lib.rs deleted file mode 100644 index 6b4362b..0000000 --- a/contracts/api-integration-tests/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// Integration test helpers (none required; tests live in tests/api.rs). diff --git a/contracts/api-integration-tests/tests/api.rs b/contracts/api-integration-tests/tests/api.rs deleted file mode 100644 index d29adba..0000000 --- a/contracts/api-integration-tests/tests/api.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Backend API integration tests against the local Soroban sandbox. -//! -//! Requires the docker-compose environment to be running: -//! docker-compose up -d -//! -//! Run with: -//! cargo test -p api-integration-tests -- --test-threads=1 - -use reqwest::blocking::Client; -use reqwest::StatusCode; -use serde_json::{json, Value}; - -const HORIZON_URL: &str = "http://localhost:8000"; -const RPC_URL: &str = "http://localhost:8000/soroban/rpc"; - -fn client() -> Client { - Client::builder() - .timeout(std::time::Duration::from_secs(10)) - .build() - .expect("failed to build HTTP client") -} - -// --------------------------------------------------------------------------- -// /health/rpc -// --------------------------------------------------------------------------- - -#[test] -fn health_rpc_returns_200() { - let resp = client() - .get(format!("{}/health", HORIZON_URL)) - .send() - .expect("request failed"); - assert_eq!(resp.status(), StatusCode::OK); -} - -#[test] -fn health_rpc_body_contains_status_ok() { - let body: Value = client() - .get(format!("{}/health", HORIZON_URL)) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - assert_eq!(body["status"], "ok", "unexpected health body: {body}"); -} - -// --------------------------------------------------------------------------- -// /invoices (Horizon transactions / Soroban RPC getLatestLedger as proxy) -// --------------------------------------------------------------------------- - -#[test] -fn invoices_get_latest_ledger_returns_200() { - let body = json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "getLatestLedger" - }); - let resp = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed"); - assert_eq!(resp.status(), StatusCode::OK); -} - -#[test] -fn invoices_get_latest_ledger_has_sequence() { - let body = json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "getLatestLedger" - }); - let resp: Value = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - assert!( - resp["result"]["sequence"].is_number(), - "missing ledger sequence: {resp}" - ); -} - -#[test] -fn invoices_simulate_transaction_missing_params_returns_error() { - let body = json!({ - "jsonrpc": "2.0", - "id": 2, - "method": "simulateTransaction", - "params": {} - }); - let resp: Value = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - // RPC spec: invalid params → error object present - assert!(resp["error"].is_object(), "expected error for missing params: {resp}"); -} - -// --------------------------------------------------------------------------- -// /disputes (Horizon accounts as a stand-in for on-chain dispute queries) -// --------------------------------------------------------------------------- - -#[test] -fn disputes_horizon_accounts_endpoint_200() { - let resp = client() - .get(format!("{}/accounts", HORIZON_URL)) - .send() - .expect("request failed"); - assert_eq!(resp.status(), StatusCode::OK); -} - -#[test] -fn disputes_horizon_accounts_has_embedded_records() { - let body: Value = client() - .get(format!("{}/accounts", HORIZON_URL)) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - assert!( - body["_embedded"]["records"].is_array(), - "unexpected accounts body: {body}" - ); -} - -#[test] -fn disputes_unknown_account_returns_404() { - let resp = client() - .get(format!("{}/accounts/GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", HORIZON_URL)) - .send() - .expect("request failed"); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); -} - -// --------------------------------------------------------------------------- -// /compliance (Soroban RPC getLedgerEntries with empty keys → 200) -// --------------------------------------------------------------------------- - -#[test] -fn compliance_get_ledger_entries_empty_keys_returns_200() { - let body = json!({ - "jsonrpc": "2.0", - "id": 3, - "method": "getLedgerEntries", - "params": { "keys": [] } - }); - let resp = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed"); - assert_eq!(resp.status(), StatusCode::OK); -} - -#[test] -fn compliance_get_ledger_entries_invalid_key_returns_rpc_error() { - let body = json!({ - "jsonrpc": "2.0", - "id": 4, - "method": "getLedgerEntries", - "params": { "keys": ["not_a_valid_xdr_key"] } - }); - let resp: Value = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - // Invalid XDR key should surface as an RPC error - assert!( - resp["error"].is_object(), - "expected error for invalid key: {resp}" - ); -} - -// --------------------------------------------------------------------------- -// Error-path: malformed JSON-RPC yields 400 / parse-error -// --------------------------------------------------------------------------- - -#[test] -fn rpc_malformed_json_returns_error_response() { - let resp = client() - .post(RPC_URL) - .header("Content-Type", "application/json") - .body("{not valid json") - .send() - .expect("request failed"); - // Soroban RPC may return 400 or 200 with an error payload - let status = resp.status(); - assert!( - status == StatusCode::BAD_REQUEST || status == StatusCode::OK, - "unexpected status {status}" - ); -} - -#[test] -fn rpc_unknown_method_returns_method_not_found_error() { - let body = json!({ - "jsonrpc": "2.0", - "id": 5, - "method": "nonExistentMethod" - }); - let resp: Value = client() - .post(RPC_URL) - .json(&body) - .send() - .expect("request failed") - .json() - .expect("non-JSON response"); - // JSON-RPC method-not-found code is -32601 - let code = resp["error"]["code"].as_i64().unwrap_or(0); - assert_eq!(code, -32601, "expected method-not-found error: {resp}"); -} - -// --------------------------------------------------------------------------- -// 503 – sandbox unavailable guard -// (tests that require a live sandbox are skipped when the node is down) -// --------------------------------------------------------------------------- - -#[test] -fn sandbox_is_reachable() { - let result = client() - .get(format!("{}/health", HORIZON_URL)) - .send(); - assert!( - result.is_ok() && result.unwrap().status().is_success(), - "Soroban sandbox is not reachable at {HORIZON_URL}. \ - Start it with: docker-compose up -d" - ); -} diff --git a/contracts/invoice/Cargo.toml b/contracts/invoice/Cargo.toml deleted file mode 100644 index 2e3baa4..0000000 --- a/contracts/invoice/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "comebackhere-invoice" -version = "1.0.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[features] -testutils = ["soroban-sdk/testutils"] - -[dependencies] -soroban-sdk = "21" - -[dev-dependencies] -soroban-sdk = { version = "21", features = ["testutils"] } diff --git a/contracts/invoice/src/lib.rs b/contracts/invoice/src/lib.rs deleted file mode 100644 index 5832b03..0000000 --- a/contracts/invoice/src/lib.rs +++ /dev/null @@ -1,630 +0,0 @@ -#![no_std] - -use soroban_sdk::{contract, contractimpl, contracttype, contracterror, Address, Env, IntoVal}; - -const MIN_AMOUNT_STROOPS: u64 = 10_000_000; - -#[contracterror] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum InvoiceError { - Unauthorized = 1, - ContractPaused = 2, - InvalidAmount = 3, - NotPending = 4, - Expired = 5, - NotFound = 6, - AlreadyInitialized = 7, - ZeroDuration = 8, - ExpiryOverflow = 9, - NotPaid = 10, - /// Cancelling a paid invoice initiates a refund; this error is returned when a - /// refund has already been requested (or the invoice is in a terminal state - /// that is not cancellable). - AlreadyRefundRequested = 11, - AmountPrecision = 12, - DuplicateNonce = 13, - AddressBlocked = 14, -} - -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum InvoiceStatus { - Pending, - Paid, - Expired, - Cancelled, - RefundRequested, - Released, -} - -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Invoice { - pub id: u64, - pub merchant: Address, - pub amount_usdc: u64, - pub gross_usdc: u64, - pub expires_at: u64, - pub status: InvoiceStatus, -} - -#[contracttype] -pub enum DataKey { - Admin, - Paused, - Invoice(u64), - NextId, - Nonce(Address, u64), - ComplianceContract, -} - -/// The invoice contract manages the lifecycle of on-chain invoices: -/// creation, payment, cancellation, and pause/unpause controls. -/// -/// Amounts are denominated in USDC stroops. A minimum of -/// `MIN_AMOUNT_STROOPS` (10 000 000, i.e. 1 USDC) is enforced to prevent -/// dust invoices. -#[contract] -pub struct InvoiceContract; - -#[contractimpl] -impl InvoiceContract { - /// Initialises the contract, setting the admin address and default state. - /// - /// # Parameters - /// - `admin`: The address that will have administrative privileges (pause/unpause). - /// - /// # Errors - /// - [`InvoiceError::AlreadyInitialized`] if `initialize` has already been called. - /// - /// # Storage written - /// Sets `Admin`, `Paused` (false), and `NextId` (1). - pub fn initialize(env: Env, admin: Address) -> Result<(), InvoiceError> { - if env.storage().instance().has(&DataKey::Admin) { - return Err(InvoiceError::AlreadyInitialized); - } - env.storage().instance().set(&DataKey::Admin, &admin); - env.storage().instance().set(&DataKey::Paused, &false); - env.storage().instance().set(&DataKey::NextId, &1u64); - Ok(()) - } - - /// Creates a new invoice and stores it in instance storage. - /// - /// Requires the merchant to have authorised this call (`merchant.require_auth()`). - /// The `nonce` is scoped per-merchant so two different merchants may reuse the - /// same nonce value without collision. - /// - /// # Parameters - /// - `merchant`: The address of the invoice creator; must authorise the transaction. - /// - `amount_usdc`: The net invoice amount in USDC stroops. Must be ≥ `MIN_AMOUNT_STROOPS` - /// (10 000 000 stroops = 1 USDC). - /// - `gross_usdc`: The gross amount (including fees) in USDC stroops. Must be ≥ `amount_usdc`. - /// - `expires_in_seconds`: Lifetime of the invoice in seconds from the current ledger - /// timestamp. Must be > 0. - /// - `nonce`: A per-merchant unique value used to prevent duplicate submissions. - /// - /// # Returns - /// The newly assigned invoice ID (a `u64` counter starting at 1). - /// - /// # Errors - /// - [`InvoiceError::ContractPaused`] if the contract is currently paused. - /// - [`InvoiceError::InvalidAmount`] if `amount_usdc` or `gross_usdc` is zero, - /// or if `gross_usdc < amount_usdc`. - /// - [`InvoiceError::AmountPrecision`] if `amount_usdc < MIN_AMOUNT_STROOPS`. - /// - [`InvoiceError::ZeroDuration`] if `expires_in_seconds` is 0. - /// - [`InvoiceError::ExpiryOverflow`] if `ledger_timestamp + expires_in_seconds` overflows `u64`. - /// - [`InvoiceError::DuplicateNonce`] if `(merchant, nonce)` has already been used. - pub fn create_invoice( - env: Env, - merchant: Address, - amount_usdc: u64, - gross_usdc: u64, - expires_in_seconds: u64, - nonce: u64, - ) -> Result { - merchant.require_auth(); - - if env - .storage() - .instance() - .get::(&DataKey::Paused) - .unwrap_or(false) - { - return Err(InvoiceError::ContractPaused); - } - - if amount_usdc == 0 || gross_usdc == 0 { - return Err(InvoiceError::InvalidAmount); - } - if amount_usdc < MIN_AMOUNT_STROOPS { - return Err(InvoiceError::AmountPrecision); - } - if gross_usdc < amount_usdc { - return Err(InvoiceError::InvalidAmount); - } - if expires_in_seconds == 0 { - return Err(InvoiceError::ZeroDuration); - } - - let nonce_key = DataKey::Nonce(merchant.clone(), nonce); - if env.storage().instance().has(&nonce_key) { - return Err(InvoiceError::DuplicateNonce); - } - - let now = env.ledger().timestamp(); - let expires_at = now - .checked_add(expires_in_seconds) - .ok_or(InvoiceError::ExpiryOverflow)?; - - env.storage().instance().set(&nonce_key, &true); - - let id: u64 = env - .storage() - .instance() - .get(&DataKey::NextId) - .unwrap_or(1u64); - let invoice = Invoice { - id, - merchant, - amount_usdc, - gross_usdc, - expires_at, - status: InvoiceStatus::Pending, - }; - env.storage().instance().set(&DataKey::Invoice(id), &invoice); - let next_id = id.checked_add(1).ok_or(InvoiceError::Overflow)?; - env.storage().instance().set(&DataKey::NextId, &next_id); - - Ok(id) - } - - /// Returns the full [`Invoice`] struct for a given ID. - /// - /// # Parameters - /// - `invoice_id`: The numeric ID returned by `create_invoice`. - /// - /// # Errors - /// - [`InvoiceError::NotFound`] if no invoice with that ID exists. - pub fn get_invoice(env: Env, invoice_id: u64) -> Result { - env.storage() - .instance() - .get(&DataKey::Invoice(invoice_id)) - .ok_or(InvoiceError::NotFound) - } - - /// Marks a `Pending` invoice as [`InvoiceStatus::Paid`]. - /// - /// Requires `payer` to authorise the call (`payer.require_auth()`). Any address - /// may act as payer — this contract does not restrict payment to the `customer` field. - /// - /// # Parameters - /// - `payer`: The address making the payment; must authorise the transaction. - /// - `invoice_id`: The ID of the invoice to pay. - /// - /// # Errors - /// - [`InvoiceError::NotFound`] if no invoice with that ID exists. - /// - [`InvoiceError::NotPending`] if the invoice is not in `Pending` status. - /// - [`InvoiceError::Expired`] if the current ledger timestamp ≥ `invoice.expires_at`. - pub fn pay_invoice(env: Env, payer: Address, invoice_id: u64) -> Result<(), InvoiceError> { - payer.require_auth(); - let mut invoice: Invoice = env - .storage() - .instance() - .get(&DataKey::Invoice(invoice_id)) - .ok_or(InvoiceError::NotFound)?; - if invoice.status != InvoiceStatus::Pending { - return Err(InvoiceError::NotPending); - } - if env.ledger().timestamp() >= invoice.expires_at { - return Err(InvoiceError::Expired); - } - - // Compliance check: reject if payer is blocked - let compliance: Option
= env - .storage() - .instance() - .get(&DataKey::ComplianceContract); - if let Some(compliance) = compliance { - let is_allowed: bool = env.invoke_contract( - &compliance, - &soroban_sdk::Symbol::new(&env, "is_allowed"), - soroban_sdk::vec![&env, payer.clone().into_val(&env)], - ); - if !is_allowed { - return Err(InvoiceError::AddressBlocked); - } - } - - invoice.status = InvoiceStatus::Paid; - env.storage() - .instance() - .set(&DataKey::Invoice(invoice_id), &invoice); - Ok(()) - } - - /// Cancels a `Pending` invoice. Only the merchant who created it may cancel it. - /// - /// Requires `caller` to authorise the call (`caller.require_auth()`). - /// - /// # Parameters - /// - `caller`: Must be the invoice's `merchant` address. - /// - `invoice_id`: The ID of the invoice to cancel. - /// - /// # Errors - /// - [`InvoiceError::NotFound`] if no invoice with that ID exists. - /// - [`InvoiceError::Unauthorized`] if `caller` is not the invoice merchant. - /// - [`InvoiceError::NotPending`] if the invoice is not in `Pending` status. - pub fn cancel_invoice( - env: Env, - caller: Address, - invoice_id: u64, - ) -> Result<(), InvoiceError> { - caller.require_auth(); - let mut invoice: Invoice = env - .storage() - .instance() - .get(&DataKey::Invoice(invoice_id)) - .ok_or(InvoiceError::NotFound)?; - if invoice.merchant != caller { - return Err(InvoiceError::Unauthorized); - } - - match invoice.status { - // No funds have moved yet — simple cancellation. - InvoiceStatus::Pending => { - invoice.status = InvoiceStatus::Cancelled; - env.storage() - .instance() - .set(&DataKey::Invoice(invoice_id), &invoice); - Ok(()) - } - // Funds are held in escrow. Cancellation initiates the refund path by - // transitioning to RefundRequested so the standard release_escrow flow - // can complete the refund. - InvoiceStatus::Paid => { - invoice.status = InvoiceStatus::RefundRequested; - env.storage() - .instance() - .set(&DataKey::Invoice(invoice_id), &invoice); - Ok(()) - } - // A refund is already in progress — no state change needed. - InvoiceStatus::RefundRequested => Err(InvoiceError::AlreadyRefundRequested), - // Terminal states: Expired, Released, Cancelled cannot be cancelled again. - InvoiceStatus::Expired | InvoiceStatus::Released | InvoiceStatus::Cancelled => { - Err(InvoiceError::NotPending) - } - } - } - - /// Pauses the contract, blocking `create_invoice` and other guarded operations - /// until `unpause` is called. Admin-only. - /// - /// Requires `admin` to authorise the call (`admin.require_auth()`). - /// - /// # Parameters - /// - `admin`: Must match the address stored at initialisation. - /// - /// # Errors - /// - [`InvoiceError::Unauthorized`] if `admin` does not match the stored admin address. - pub fn pause(env: Env, admin: Address) -> Result<(), InvoiceError> { - admin.require_auth(); - let stored: Address = env - .storage() - .instance() - .get(&DataKey::Admin) - .unwrap(); - if stored != admin { - return Err(InvoiceError::Unauthorized); - } - env.storage().instance().set(&DataKey::Paused, &true); - Ok(()) - } - - /// Unpauses the contract, restoring all guarded operations. Admin-only. - /// - /// Requires `admin` to authorise the call (`admin.require_auth()`). - /// - /// # Parameters - /// - `admin`: Must match the address stored at initialisation. - /// - /// # Errors - /// - [`InvoiceError::Unauthorized`] if `admin` does not match the stored admin address. - pub fn unpause(env: Env, admin: Address) -> Result<(), InvoiceError> { - admin.require_auth(); - let stored: Address = env - .storage() - .instance() - .get(&DataKey::Admin) - .unwrap(); - if stored != admin { - return Err(InvoiceError::Unauthorized); - } - env.storage().instance().set(&DataKey::Paused, &false); - Ok(()) - } - - /// Configure the compliance contract address (admin only). - pub fn set_compliance( - env: Env, - admin: Address, - compliance: Address, - ) -> Result<(), InvoiceError> { - admin.require_auth(); - let stored: Address = env - .storage() - .instance() - .get(&DataKey::Admin) - .unwrap(); - if stored != admin { - return Err(InvoiceError::Unauthorized); - } - env.storage() - .instance() - .set(&DataKey::ComplianceContract, &compliance); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use soroban_sdk::testutils::{Address as _, Ledger}; - use soroban_sdk::Env; - - fn setup() -> (Env, Address, Address) { - let env = Env::default(); - env.mock_all_auths(); - let contract_id = env.register_contract(None, InvoiceContract); - let admin = Address::generate(&env); - InvoiceContractClient::new(&env, &contract_id).initialize(&admin); - env.ledger().set_timestamp(1000); - (env, contract_id, admin) - } - - // ── existing tests (updated for new signature) ─────────────────────────── - - #[test] - fn test_create_invoice_min_amount_passes() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let result = c.create_invoice(&merchant, &10_000_000u64, &10_500_000u64, &3600u64, &1u64); - assert_eq!(result, 1); - } - - #[test] - fn test_create_invoice_below_min_returns_error() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let result = c.try_create_invoice(&merchant, &9_999_999u64, &10_499_999u64, &3600u64, &1u64); - assert_eq!(result, Err(Ok(InvoiceError::AmountPrecision))); - } - - #[test] - fn test_create_invoice_zero_amount_returns_error() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let result = c.try_create_invoice(&merchant, &0u64, &0u64, &3600u64, &1u64); - assert_eq!(result, Err(Ok(InvoiceError::InvalidAmount))); - } - - // ── InvoiceError boundary tests ────────────────────────────────────────── - - /// Unauthorized: non-merchant caller tries to cancel the invoice. - #[test] - fn test_unauthorized_cancel_by_non_merchant() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let stranger = Address::generate(&env); - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - let res = c.try_cancel_invoice(&stranger, &id); - assert_eq!(res, Err(Ok(InvoiceError::Unauthorized))); - } - - /// InvalidAmount: gross_usdc less than amount_usdc. - #[test] - fn test_invalid_amount_gross_less_than_net() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - // gross < amount → InvalidAmount - let res = c.try_create_invoice(&merchant, &20_000_000u64, &10_000_000u64, &3600u64, &1u64); - assert_eq!(res, Err(Ok(InvoiceError::InvalidAmount))); - } - - /// Expired: pay an invoice after its expiry timestamp. - #[test] - fn test_expired_pay_after_expiry() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let payer = Address::generate(&env); - // timestamp=1000, expires_in=1 → expires_at=1001 - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &1u64, &1u64); - env.ledger().set_timestamp(1001); - let res = c.try_pay_invoice(&payer, &id); - assert_eq!(res, Err(Ok(InvoiceError::Expired))); - } - - /// Expired boundary: paying at exactly the expiry timestamp is also expired. - #[test] - fn test_expired_pay_at_exact_expiry_boundary() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let payer = Address::generate(&env); - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &60u64, &1u64); - // expires_at = 1000 + 60 = 1060; >= check means 1060 is expired - env.ledger().set_timestamp(1060); - let res = c.try_pay_invoice(&payer, &id); - assert_eq!(res, Err(Ok(InvoiceError::Expired))); - } - - /// NotFound: get an invoice that does not exist. - #[test] - fn test_not_found_get_nonexistent_invoice() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let res = c.try_get_invoice(&999u64); - assert_eq!(res, Err(Ok(InvoiceError::NotFound))); - } - - /// NotFound: pay an invoice that does not exist. - #[test] - fn test_not_found_pay_nonexistent_invoice() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let payer = Address::generate(&env); - let res = c.try_pay_invoice(&payer, &999u64); - assert_eq!(res, Err(Ok(InvoiceError::NotFound))); - } - - /// DuplicateNonce: same merchant + nonce used twice. - #[test] - fn test_duplicate_nonce_same_merchant() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &42u64); - let res = c.try_create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &42u64); - assert_eq!(res, Err(Ok(InvoiceError::DuplicateNonce))); - } - - /// Different merchants may reuse the same nonce (no collision). - #[test] - fn test_duplicate_nonce_different_merchants_allowed() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let m1 = Address::generate(&env); - let m2 = Address::generate(&env); - c.create_invoice(&m1, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - let id2 = c.create_invoice(&m2, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - assert_eq!(id2, 2); - } - - /// AmountPrecision: exactly one stroop below the minimum. - #[test] - fn test_amount_precision_below_minimum() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let res = c.try_create_invoice( - &merchant, - &(MIN_AMOUNT_STROOPS - 1), - &(MIN_AMOUNT_STROOPS - 1), - &3600u64, - &1u64, - ); - assert_eq!(res, Err(Ok(InvoiceError::AmountPrecision))); - } - - /// AmountPrecision: value of 1 is non-zero but below minimum. - #[test] - fn test_amount_precision_value_of_one() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let res = c.try_create_invoice(&merchant, &1u64, &1u64, &3600u64, &1u64); - assert_eq!(res, Err(Ok(InvoiceError::AmountPrecision))); - } - - /// ContractPaused: create_invoice is blocked when the contract is paused. - #[test] - fn test_contract_paused_blocks_create_invoice() { - let (env, cid, admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - c.pause(&admin); - let res = c.try_create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - assert_eq!(res, Err(Ok(InvoiceError::ContractPaused))); - } - - // ── cancellation refund-path tests ─────────────────────────────────────── - - /// Cancelling a Pending invoice (no funds moved) succeeds and sets Cancelled. - #[test] - fn test_cancel_pending_invoice_no_fund_movement() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - - c.cancel_invoice(&merchant, &id); - - let invoice = c.get_invoice(&id); - assert_eq!(invoice.status, InvoiceStatus::Cancelled); - } - - /// Cancelling a Paid invoice initiates the refund path (→ RefundRequested). - /// Funds are not lost; the existing release_escrow flow can now complete the - /// refund from RefundRequested state. - #[test] - fn test_cancel_paid_invoice_transitions_to_refund_requested() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let payer = Address::generate(&env); - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - - // Pay the invoice (funds are now escrowed). - c.pay_invoice(&payer, &id); - let after_pay = c.get_invoice(&id); - assert_eq!(after_pay.status, InvoiceStatus::Paid); - - // Merchant cancels — this should NOT leave funds stuck; it opens the - // refund path instead of doing nothing or erroring opaquely. - c.cancel_invoice(&merchant, &id); - - let after_cancel = c.get_invoice(&id); - assert_eq!( - after_cancel.status, - InvoiceStatus::RefundRequested, - "cancelling a paid invoice must initiate the refund path" - ); - } - - /// Cancelling an invoice where a refund is already in progress returns - /// AlreadyRefundRequested so callers know the refund path is already open. - #[test] - fn test_cancel_refund_requested_invoice_returns_already_refund_requested() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - let payer = Address::generate(&env); - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &1u64); - - c.pay_invoice(&payer, &id); - // First cancel: opens refund path. - c.cancel_invoice(&merchant, &id); - // Second cancel: refund already in progress. - let res = c.try_cancel_invoice(&merchant, &id); - assert_eq!(res, Err(Ok(InvoiceError::AlreadyRefundRequested))); - } - - /// Cancelling an Expired invoice returns NotPending (terminal state). - #[test] - fn test_cancel_expired_invoice_returns_not_pending() { - let (env, cid, _admin) = setup(); - let c = InvoiceContractClient::new(&env, &cid); - let merchant = Address::generate(&env); - // expires_in=1 → expires_at = 1001 - let id = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &1u64, &1u64); - // Advance past expiry (we can't call batch_expire here, but status hasn't - // changed yet; the contract's pay_invoice enforces expiry, not storage). - // To simulate an expired invoice we manually verify the guard behaviour - // by paying at a valid timestamp first then checking we can't cancel. - // Here we just verify that a Pending invoice at valid time can be cancelled: - c.cancel_invoice(&merchant, &id); - // Re-create an invoice and check cancelling an already-Cancelled one returns NotPending. - let id2 = c.create_invoice(&merchant, &10_000_000u64, &10_000_000u64, &3600u64, &2u64); - c.cancel_invoice(&merchant, &id2); - let res = c.try_cancel_invoice(&merchant, &id2); - assert_eq!(res, Err(Ok(InvoiceError::NotPending))); - } -} diff --git a/contracts/settlement/Cargo.toml b/contracts/settlement/Cargo.toml deleted file mode 100644 index 9a32fe3..0000000 --- a/contracts/settlement/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "comebackhere-settlement" -version = "1.0.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[features] -testutils = ["soroban-sdk/testutils"] - -[dependencies] -soroban-sdk = "21" - -[dev-dependencies] -soroban-sdk = { version = "21", features = ["testutils"] } -proptest = "1.4" diff --git a/contracts/settlement/src/integration_settlement_multisig.rs b/contracts/settlement/src/integration_settlement_multisig.rs deleted file mode 100644 index 7c57bf3..0000000 --- a/contracts/settlement/src/integration_settlement_multisig.rs +++ /dev/null @@ -1,186 +0,0 @@ -#![cfg(test)] - -//! Multi-sig propose/approve integration coverage for the legacy settlement -//! contract, mirroring -//! `COMEBACKHERE-contracts/contracts/treasury/src/integration_settlement_multisig.rs` -//! in the canonical tree (see #31 / CONTRIBUTING.md on keeping the two trees -//! in sync). -//! -//! Unlike the canonical treasury contract, this legacy contract has no -//! separate `execute_settlement` entry point: `approve_settlement` returns -//! the accumulated `approval_weight` and `threshold` directly, and a caller -//! reaching quorum (`approval_weight >= threshold`) *is* the execute signal -//! this simpler contract exposes. These tests treat "quorum reached" as the -//! execute step the canonical tests exercise explicitly via -//! `execute_settlement`. - -use super::*; -use soroban_sdk::{testutils::Address as _, Address, Env}; - -fn setup_env() -> (Env, Address) { - let e = Env::default(); - e.mock_all_auths(); - let contract_id = e.register_contract(None, SettlementContract); - (e, contract_id) -} - -fn make_client<'a>(e: &'a Env, id: &Address) -> SettlementContractClient<'a> { - SettlementContractClient::new(e, id) -} - -#[test] -fn test_multisig_propose_collect_2_of_3_reaches_quorum() { - let (e, id) = setup_env(); - let c = make_client(&e, &id); - - let signer_a = Address::generate(&e); - let signer_b = Address::generate(&e); - let signer_c = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![ - &e, - (signer_a.clone(), 1u64), - (signer_b.clone(), 1u64), - (signer_c.clone(), 1u64), - ], - &2u64, - ); - - let sid = c.propose(&signer_a, &merchant, &5_000_000u64); - - let r1 = c.approve_settlement(&signer_a, &sid); - assert_eq!(r1.approval_weight, 1); - assert!( - r1.approval_weight < r1.threshold, - "quorum should not be reached after 1-of-2 approvals" - ); - - let r2 = c.approve_settlement(&signer_b, &sid); - assert_eq!(r2.approval_weight, 2); - assert!( - r2.approval_weight >= r2.threshold, - "quorum should be reached after 2-of-2 approvals" - ); -} - -#[test] -fn test_single_signer_insufficient_for_threshold_2() { - let (e, id) = setup_env(); - let c = make_client(&e, &id); - - let signer_a = Address::generate(&e); - let signer_b = Address::generate(&e); - let signer_c = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![ - &e, - (signer_a.clone(), 1u64), - (signer_b.clone(), 1u64), - (signer_c.clone(), 1u64), - ], - &2u64, - ); - - let sid = c.propose(&signer_a, &merchant, &1_000_000u64); - let res = c.approve_settlement(&signer_a, &sid); - - assert!( - res.approval_weight < res.threshold, - "a single signer must not be able to reach a threshold of 2 alone" - ); -} - -#[test] -fn test_weighted_signers_reach_threshold() { - let (e, id) = setup_env(); - let c = make_client(&e, &id); - - let signer_a = Address::generate(&e); - let signer_b = Address::generate(&e); - let signer_c = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![ - &e, - (signer_a.clone(), 2u64), - (signer_b.clone(), 1u64), - (signer_c.clone(), 1u64), - ], - &2u64, - ); - - let sid = c.propose(&signer_a, &merchant, &3_000_000u64); - let res = c.approve_settlement(&signer_a, &sid); - - assert_eq!(res.approval_weight, 2); - assert!( - res.approval_weight >= res.threshold, - "a single signer with weight=2 should meet threshold=2 alone" - ); -} - -#[test] -fn test_multiple_settlements_independent_approvals() { - let (e, id) = setup_env(); - let c = make_client(&e, &id); - - let signer_a = Address::generate(&e); - let signer_b = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (signer_a.clone(), 1u64), (signer_b.clone(), 1u64)], - &2u64, - ); - - let s1 = c.propose(&signer_a, &merchant, &1_000_000u64); - let s2 = c.propose(&signer_a, &merchant, &2_000_000u64); - - c.approve_settlement(&signer_a, &s1); - let r1 = c.approve_settlement(&signer_b, &s1); - assert!( - r1.approval_weight >= r1.threshold, - "s1 should reach quorum" - ); - - let r2 = c.approve_settlement(&signer_a, &s2); - assert!( - r2.approval_weight < r2.threshold, - "s2 should still be short of quorum with only one approval" - ); -} - -#[test] -fn test_quorum_reached_settlement_can_still_be_cancelled() { - // The legacy contract has no `execute_settlement` call that flips - // status to `Executed`; a settlement stays `Pending` (and thus - // cancellable) even after quorum is reached on-chain. Execution is - // expected to be driven by an off-chain caller reacting to - // `ApproveResult`. - let (e, id) = setup_env(); - let c = make_client(&e, &id); - - let signer_a = Address::generate(&e); - let signer_b = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (signer_a.clone(), 1u64), (signer_b.clone(), 1u64)], - &2u64, - ); - - let sid = c.propose(&signer_a, &merchant, &10_000_000u64); - c.approve_settlement(&signer_a, &sid); - let res = c.approve_settlement(&signer_b, &sid); - assert!(res.approval_weight >= res.threshold); - - c.cancel(&signer_a, &sid); - - let cancel_again = c.try_cancel(&signer_a, &sid); - assert_eq!(cancel_again, Err(Ok(SettlementError::NotPending))); -} diff --git a/contracts/settlement/src/lib.rs b/contracts/settlement/src/lib.rs deleted file mode 100644 index 2237bef..0000000 --- a/contracts/settlement/src/lib.rs +++ /dev/null @@ -1,460 +0,0 @@ -#![no_std] - -use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, Env, Vec}; - -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum SettlementStatus { - Pending, - Executed, - Cancelled, -} - -#[contracttype] -#[derive(Clone)] -pub struct Settlement { - pub merchant: Address, - pub amount: u64, - pub status: SettlementStatus, - pub approval_weight: u64, - pub approvals: Vec
, - pub proposer: Address, -} - -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ApproveResult { - pub approval_weight: u64, - pub threshold: u64, -} - -#[contracterror] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum SettlementError { - NotFound = 1, - Unauthorized = 2, - AlreadyApproved = 3, - NotPending = 4, - DuplicateSigner = 5, - InvalidWeightSum = 6, -} - -#[contracttype] -pub enum DataKey { - Threshold, - Signer(Address), - Settlement(u64), - NextId, -} - -#[contract] -pub struct SettlementContract; - -#[contractimpl] -impl SettlementContract { - /// Initialize with signers (address, weight pairs) and approval threshold. - pub fn initialize( - e: Env, - signers: Vec<(Address, u64)>, - threshold: u64, - ) -> Result<(), SettlementError> { - let mut seen: Vec
= Vec::new(&e); - let mut total_weight: u64 = 0; - for (signer, weight) in signers.iter() { - if seen.contains(&signer) { - return Err(SettlementError::DuplicateSigner); - } - seen.push_back(signer.clone()); - total_weight += weight; - } - if total_weight < threshold { - return Err(SettlementError::InvalidWeightSum); - } - e.storage().instance().set(&DataKey::Threshold, &threshold); - e.storage().instance().set(&DataKey::NextId, &1u64); - for (signer, weight) in signers.iter() { - e.storage() - .instance() - .set(&DataKey::Signer(signer.clone()), &weight); - } - Ok(()) - } - - /// Propose a new settlement; returns the settlement ID. - pub fn propose(e: Env, proposer: Address, merchant: Address, amount: u64) -> u64 { - proposer.require_auth(); - let id: u64 = e - .storage() - .instance() - .get(&DataKey::NextId) - .unwrap_or(1u64); - let s = Settlement { - merchant, - amount, - status: SettlementStatus::Pending, - approval_weight: 0, - approvals: Vec::new(&e), - proposer: proposer.clone(), - }; - e.storage().instance().set(&DataKey::Settlement(id), &s); - e.storage().instance().set(&DataKey::NextId, &(id + 1)); - id - } - - /// Approve a pending settlement. - /// - /// Returns the current `approval_weight` and required `threshold` so callers - /// can track progress toward execution. - /// - /// Errors: - /// - `NotFound` – settlement ID does not exist - /// - `Unauthorized` – signer has no registered weight - /// - `AlreadyApproved` – signer has already approved this settlement - /// - `NotPending` – settlement is not in Pending status - pub fn approve_settlement( - e: Env, - signer: Address, - settlement_id: u64, - ) -> Result { - signer.require_auth(); - - let weight: u64 = e - .storage() - .instance() - .get(&DataKey::Signer(signer.clone())) - .unwrap_or(0); - if weight == 0 { - return Err(SettlementError::Unauthorized); - } - - let mut settlement: Settlement = e - .storage() - .instance() - .get(&DataKey::Settlement(settlement_id)) - .ok_or(SettlementError::NotFound)?; - - if settlement.status != SettlementStatus::Pending { - return Err(SettlementError::NotPending); - } - - if settlement.approvals.contains(&signer) { - return Err(SettlementError::AlreadyApproved); - } - - settlement.approval_weight += weight; - settlement.approvals.push_back(signer); - e.storage() - .instance() - .set(&DataKey::Settlement(settlement_id), &settlement); - - let threshold: u64 = e - .storage() - .instance() - .get(&DataKey::Threshold) - .unwrap_or(0); - - Ok(ApproveResult { - approval_weight: settlement.approval_weight, - threshold, - }) - } - - /// Cancel a pending settlement. - /// - /// Only the original proposer or an authorized signer may cancel. - /// - /// Errors: - /// - `NotFound` – settlement ID does not exist - /// - `Unauthorized` – caller is neither proposer nor authorized signer - /// - `NotPending` – settlement is not in Pending status - pub fn cancel(e: Env, caller: Address, settlement_id: u64) -> Result<(), SettlementError> { - caller.require_auth(); - - let mut settlement: Settlement = e - .storage() - .instance() - .get(&DataKey::Settlement(settlement_id)) - .ok_or(SettlementError::NotFound)?; - - if settlement.status != SettlementStatus::Pending { - return Err(SettlementError::NotPending); - } - - // Allow original proposer or an authorized signer to cancel - let is_proposer = settlement.proposer == caller; - let weight: u64 = e - .storage() - .instance() - .get(&DataKey::Signer(caller.clone())) - .unwrap_or(0); - if !is_proposer && weight == 0 { - return Err(SettlementError::Unauthorized); - } - - settlement.status = SettlementStatus::Cancelled; - e.storage() - .instance() - .set(&DataKey::Settlement(settlement_id), &settlement); - Ok(()) - } -} - -#[cfg(test)] -mod integration_settlement_multisig; - -#[cfg(test)] -mod tests { - use super::*; - use soroban_sdk::{testutils::Address as _, Env}; - - fn setup() -> (Env, Address) { - let e = Env::default(); - e.mock_all_auths(); - let id = e.register_contract(None, SettlementContract); - (e, id) - } - - #[test] - fn test_approve_increments_weight_and_returns_threshold() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let signer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize(&soroban_sdk::vec![&e, (signer.clone(), 2u64)], &2u64); - let sid = c.propose(&signer, &merchant, &1000u64); - - let res = c.approve_settlement(&signer, &sid); - assert_eq!(res.approval_weight, 2); - assert_eq!(res.threshold, 2); - } - - #[test] - fn test_approve_unauthorized_signer() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let authorized = Address::generate(&e); - let stranger = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize(&soroban_sdk::vec![&e, (authorized.clone(), 1u64)], &1u64); - let sid = c.propose(&authorized, &merchant, &500u64); - - let res = c.try_approve_settlement(&stranger, &sid); - assert_eq!(res, Err(Ok(SettlementError::Unauthorized))); - } - - #[test] - fn test_approve_already_approved() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let signer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize(&soroban_sdk::vec![&e, (signer.clone(), 1u64)], &1u64); - let sid = c.propose(&signer, &merchant, &100u64); - - c.approve_settlement(&signer, &sid); - let res = c.try_approve_settlement(&signer, &sid); - assert_eq!(res, Err(Ok(SettlementError::AlreadyApproved))); - } - - #[test] - fn test_approve_not_found() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let signer = Address::generate(&e); - - c.initialize(&soroban_sdk::vec![&e, (signer.clone(), 1u64)], &1u64); - - let res = c.try_approve_settlement(&signer, &99u64); - assert_eq!(res, Err(Ok(SettlementError::NotFound))); - } - - #[test] - fn test_approve_not_pending() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let signer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize(&soroban_sdk::vec![&e, (signer.clone(), 1u64)], &1u64); - let sid = c.propose(&signer, &merchant, &200u64); - c.cancel(&signer, &sid); - - let res = c.try_approve_settlement(&signer, &sid); - assert_eq!(res, Err(Ok(SettlementError::NotPending))); - } - - #[test] - fn test_multiple_signers_accumulate_weight() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let s1 = Address::generate(&e); - let s2 = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (s1.clone(), 1u64), (s2.clone(), 2u64)], - &3u64, - ); - let sid = c.propose(&s1, &merchant, &500u64); - - let r1 = c.approve_settlement(&s1, &sid); - assert_eq!(r1.approval_weight, 1); - - let r2 = c.approve_settlement(&s2, &sid); - assert_eq!(r2.approval_weight, 3); - assert_eq!(r2.threshold, 3); - } - - // ── cancel authorization tests ────────────────────────────────────────── - - #[test] - fn test_cancel_own_settlement_succeeds() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let proposer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (proposer.clone(), 1u64)], - &1u64, - ); - let sid = c.propose(&proposer, &merchant, &1000u64); - - // Proposer cancels their own settlement — should succeed - c.cancel(&proposer, &sid); - - // Verify it is now cancelled by trying to cancel again - let res = c.try_cancel(&proposer, &sid); - assert_eq!(res, Err(Ok(SettlementError::NotPending))); - } - - #[test] - fn test_cancel_unauthorized_stranger() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let proposer = Address::generate(&e); - let stranger = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (proposer.clone(), 1u64)], - &1u64, - ); - let sid = c.propose(&proposer, &merchant, &1000u64); - - // Stranger (not proposer, not authorized signer) attempts cancel - let res = c.try_cancel(&stranger, &sid); - assert_eq!(res, Err(Ok(SettlementError::Unauthorized))); - } - - #[test] - fn test_cancel_authorized_signer_can_cancel() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let proposer = Address::generate(&e); - let signer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (proposer.clone(), 1u64), (signer.clone(), 1u64)], - &2u64, - ); - let sid = c.propose(&proposer, &merchant, &1000u64); - - // Authorized signer (not proposer) can also cancel - c.cancel(&signer, &sid); - - let res = c.try_cancel(&signer, &sid); - assert_eq!(res, Err(Ok(SettlementError::NotPending))); - } - - #[test] - fn test_cancel_not_found() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let signer = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (signer.clone(), 1u64)], - &1u64, - ); - - let res = c.try_cancel(&signer, &99u64); - assert_eq!(res, Err(Ok(SettlementError::NotFound))); - } - - #[test] - fn test_cancel_not_pending_fails() { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - let proposer = Address::generate(&e); - let merchant = Address::generate(&e); - - c.initialize( - &soroban_sdk::vec![&e, (proposer.clone(), 1u64)], - &1u64, - ); - let sid = c.propose(&proposer, &merchant, &1000u64); - - // Cancel succeeds - c.cancel(&proposer, &sid); - - // Cancelling an already-cancelled (or executed) settlement fails with NotPending - let res = c.try_cancel(&proposer, &sid); - assert_eq!(res, Err(Ok(SettlementError::NotPending))); - } - - use proptest::prelude::*; - extern crate std; - use std::collections::HashSet; - use std::vec::Vec; - - proptest! { - #![proptest_config(ProptestConfig::with_cases(50))] - - #[test] - fn prop_settlement_quorum_logic( - weights in prop::collection::vec(1u64..100u64, 1..10), - threshold in 1u64..500u64, - approver_indices in prop::collection::vec(0usize..10, 0..10), - ) { - let (e, id) = setup(); - let c = SettlementContractClient::new(&e, &id); - - let merchant = Address::generate(&e); - let mut signers_vec = soroban_sdk::Vec::new(&e); - let mut signer_list: Vec<(Address, u64)> = Vec::new(); - - for &w in &weights { - let s = Address::generate(&e); - signers_vec.push_back((s.clone(), w)); - signer_list.push((s, w)); - } - - c.initialize(&signers_vec, &threshold); - let proposer = &signer_list[0].0; - let sid = c.propose(proposer, &merchant, &1000u64); - - let mut used_indices = HashSet::new(); - let mut expected_accumulated_weight = 0u64; - - for &raw_idx in &approver_indices { - let idx = raw_idx % signer_list.len(); - if used_indices.insert(idx) { - let (ref signer_addr, weight) = signer_list[idx]; - let res = c.approve_settlement(signer_addr, &sid); - expected_accumulated_weight += weight; - prop_assert_eq!(res.approval_weight, expected_accumulated_weight); - prop_assert_eq!(res.threshold, threshold); - } - } - - let quorum_met = expected_accumulated_weight >= threshold; - prop_assert_eq!(expected_accumulated_weight >= threshold, quorum_met); - } - } -} diff --git a/docs/adr-0001-dual-source-trees.md b/docs/adr-0001-dual-source-trees.md index dd996e3..e6b1001 100644 --- a/docs/adr-0001-dual-source-trees.md +++ b/docs/adr-0001-dual-source-trees.md @@ -10,12 +10,13 @@ The repository contains two parallel source trees for each layer of the stack: | Layer | Canonical tree | Legacy tree | | --- | --- | --- | -| Contracts (Rust) | `COMEBACKHERE-contracts/` | `contracts/` | +| Contracts (Rust) | `COMEBACKHERE-contracts/` | Removed in PR #948 | | Backend (Node/Express) | `comebackhere-backend/` | `backend/` | | Frontend (React) | `comebackhere-frontend/` | `frontend/` | -The legacy trees (`contracts/`, `backend/`, `frontend/`) were the original -in-tree copies of the source code. When the project reorganised to use +The legacy trees were the original in-tree copies of the source code. The +contract mirror was removed in PR #948; the backend and frontend mirrors remain. +When the project reorganised to use dedicated CI workflows per layer (`.github/workflows/ci-contracts.yml`, `ci.yml`), new canonical directories were introduced with the `COMEBACKHERE-` prefix. The legacy trees were kept in place rather than deleted. @@ -24,17 +25,15 @@ prefix. The legacy trees were kept in place rather than deleted. 1. **CI restructure.** The canonical trees are wired into GitHub Actions workflows that run `cargo test`, `npm ci`, `tsc --noEmit`, and - `npm run build` on every PR. The legacy trees have no equivalent - dedicated CI — they rely on file-matching jobs that may or may not - cover a given change. + `npm run build` on every PR. The former contract mirror had no + equivalent dedicated CI coverage. -2. **Backward-compatible doc references.** Documentation such as - `docs/error-codes.md` cites `contracts/invoice/src/lib.rs` as the - source of `InvoiceError`. Removing the legacy tree without updating - every reference would break cross-links. +2. **Backward-compatible doc references.** Documentation once cited + files in the contract mirror. Those references now point to canonical + contract sources or identify historical error codes. -3. **Reviewer familiarity.** Contributors accustomed to the old paths - could continue reviewing changes at the same locations during the +3. **Reviewer familiarity.** Contributors accustomed to the old backend + and frontend paths could continue reviewing changes there during the transition period. 4. **No-breaking-change migration.** Deleting the legacy trees in a @@ -44,9 +43,8 @@ prefix. The legacy trees were kept in place rather than deleted. ### What each tree contains The canonical trees carry the full current source plus event modules, -benchmark suites, integration tests, and ABI-generation hooks. The legacy -trees carry an older snapshot — some modules (e.g. `events.rs`, -`benchmark.rs`, `test.rs`) have never been back-ported. +benchmark suites, integration tests, and ABI-generation hooks. The +remaining backend and frontend mirrors carry older snapshots. See [docs/contract-tree-feature-parity.md](./contract-tree-feature-parity.md) for a detailed comparison. @@ -55,19 +53,22 @@ for a detailed comparison. ## Decision -We maintain the dual-tree structure until the following conditions are met: +The contract migration is complete: the legacy contract tree was removed +in PR #948, and its documentation and CI references were updated. The +backend and frontend mirrors remain until their migrations are complete. +For those remaining mirrors: 1. **All doc references** to legacy paths are updated or removed. 2. **Full feature parity** is achieved (see the migration checklist in `contract-tree-feature-parity.md`). 3. **No open PRs** target the legacy trees. -4. A **single PR** deletes the legacy trees and updates any remaining +4. A **single PR** removes each remaining legacy tree and updates its references. Until then, the rule is simple: -> New work targets the `COMEBACKHERE-*` tree. The legacy tree is a valid -> PR target only for narrow fixes to files that CI already covers. +> New work targets the `COMEBACKHERE-*` tree. Follow the repository's +> contributor guidance for any remaining backend or frontend mirrors. --- diff --git a/docs/adr-0002-dual-tree-removal-plan.md b/docs/adr-0002-dual-tree-removal-plan.md index 4083b0c..a9bc287 100644 --- a/docs/adr-0002-dual-tree-removal-plan.md +++ b/docs/adr-0002-dual-tree-removal-plan.md @@ -2,7 +2,7 @@ ## Status -Proposed +In progress. The legacy contract tree was removed in PR #948; backend and frontend migration work remains. ## Context @@ -22,7 +22,7 @@ Without a concrete plan and timeline, the migration risk stalling indefinitely. ### Remaining Parity Gaps -Based on [docs/contract-tree-feature-parity.md](./contract-tree-feature-parity.md), the legacy `contracts/` tree lacks: +Before its removal, the legacy contract tree differed from the canonical workspace as follows (see [the migration record](./contract-tree-feature-parity.md)): | Module | Missing in Legacy Tree | Issue(s) | | --- | --- | --- | @@ -41,8 +41,8 @@ For `backend/` and `frontend/`, both trees are substantially in sync but the leg - [ ] Complete [#186](https://github.com/WHEELBACK/COMEBACKHERE/issues/186) — Add event indexing to canonical invoice and treasury contracts. - [ ] Complete [#182](https://github.com/WHEELBACK/COMEBACKHERE/issues/182) — Add benchmarks to treasury contract. -- [ ] Add `events.rs`, `test.rs`, and `tests.rs` to legacy invoice contract OR document that legacy invoice is deprecated. -- [ ] Audit `docs/error-codes.md` and other doc files that reference `contracts/invoice/src/lib.rs`; update all refs to point to `COMEBACKHERE-contracts/contracts/invoice/src/lib.rs`. +- [x] Remove the legacy contract tree rather than back-porting features to the old invoice copy. +- [x] Update error-code documentation to reference canonical contract sources and identify historical error codes. - [ ] Update CONTRIBUTING.md to explicitly state legacy trees are deprecated as of this milestone. **Exit Criteria:** @@ -55,8 +55,8 @@ For `backend/` and `frontend/`, both trees are substantially in sync but the leg - [ ] Verify `backend/` is byte-identical to `comebackhere-backend/` (or document intentional divergence). - [ ] Verify `frontend/` is byte-identical to `comebackhere-frontend/` (or document intentional divergence). -- [ ] Resolve `contracts/settlement/` vs `COMEBACKHERE-contracts/contracts/treasury/` naming — decide whether legacy settlement is kept as a separate module or fully deprecated. -- [ ] Close or migrate any open PRs targeting `contracts/`, `backend/`, or `frontend/` to canonical paths. +- [x] Resolve the settlement/treasury naming difference by removing the legacy settlement contract as superseded. +- [x] Confirm no open PRs target `contracts/`; migrate any open PRs targeting the remaining `backend/` or `frontend/` mirrors to canonical paths. **Exit Criteria:** - No open PRs target the legacy trees. @@ -70,7 +70,7 @@ For `backend/` and `frontend/`, both trees are substantially in sync but the leg - [ ] Run `cargo test --manifest-path COMEBACKHERE-contracts/Cargo.toml` to confirm full canonical contract coverage. - [ ] Verify all contract features in the canonical tree are exercised by CI or integration tests. - [ ] Run backend and frontend test suites to confirm they pass on canonical trees. -- [ ] Update CI to explicitly reject any new PRs that add code to `contracts/`, `backend/`, or `frontend/`. +- [x] Update CI to reject changes to the removed `contracts/` tree. Backend and frontend mirror guards remain part of their migration work. **Exit Criteria:** - CI coverage for canonical trees is documented and green. @@ -80,14 +80,13 @@ For `backend/` and `frontend/`, both trees are substantially in sync but the leg A mirrored tree is safe to delete when: -1. **No doc references remain** that would 404 or become invalid (see Milestone 1). -2. **All features are ported to the canonical tree** or explicitly removed (see Milestone 2). -3. **No open PRs target that tree** (see Milestone 2). -4. **CI is updated to reject new additions** to the legacy tree (see Milestone 3). -5. **A single-commit removal PR** is opened that: - - Deletes the legacy tree directory. - - Updates any remaining ARCHITECTURE.md, CONTRIBUTING.md, and README.md refs. - - Adds an entry to CHANGELOG.md under `[Unreleased] > Removed` documenting the tree deletion. +The contract-tree deletion criteria were completed in PR #948: its obsolete behavior was superseded or removed, contract-specific documentation and CI references were updated, and the tree was deleted. The criteria below continue to apply to the backend and frontend mirrors. + +1. **No doc references remain** that would 404 or become invalid. +2. **Features are ported to their canonical trees** or explicitly removed. +3. **No open PRs target the remaining mirrors.** +4. **CI is updated** for the remaining mirrors' migration status. +5. A removal PR updates remaining references and records the deletion under `[Unreleased] > Removed`. ### Proposed Deletion Order diff --git a/docs/contract-tree-feature-parity.md b/docs/contract-tree-feature-parity.md index 232c37b..4bee3f4 100644 --- a/docs/contract-tree-feature-parity.md +++ b/docs/contract-tree-feature-parity.md @@ -1,4 +1,6 @@ -# Contract Source Tree Feature Parity +# Contract Source Tree Migration Record + +The legacy contract workspace was removed in PR #948. `COMEBACKHERE-contracts/` is now the only in-repository contract source and is covered by the canonical contract CI workflow. The inventory below records the differences that led to the removal; it is historical, not a current parity checklist. This page tracks feature parity between the two in-tree contract workspaces: @@ -49,27 +51,6 @@ This page tracks feature parity between the two in-tree contract workspaces: --- -## When to target the legacy tree - -The legacy `contracts/` tree is a valid PR target only when: - -1. The change is a **narrow fix** to a file that CI already covers - (e.g. a doc tweak or a small Rust fix in `contracts/invoice/src/lib.rs`). -2. You are intentionally maintaining backward compatibility with the - legacy workspace for a transitional period. - -For **all new features, bug fixes, and contract changes**, target -`COMEBACKHERE-contracts/` to inherit full CI coverage. - ---- - -## Migration checklist - -As features are ported or parity issues resolved, update this table by -flipping the status column. The goal is full parity — at which point the -legacy tree can be removed. +## Historical notes -- [x] Port `api-integration-tests` to canonical workspace (or remove) -- [ ] Resolve `settlement` vs `treasury` naming alignment -- [ ] Add `events.rs` and `test.rs` / `tests.rs` to legacy invoice (or remove legacy tree) -- [ ] Remove legacy tree once no doc references remain +The historical inventory above records the legacy invoice gaps and the treasury/compliance features that were not mirrored. The settlement-only legacy contract was superseded by the canonical treasury contract. New work belongs in `COMEBACKHERE-contracts/`. diff --git a/docs/error-codes.md b/docs/error-codes.md index 8beac29..f6ee6b5 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -10,7 +10,7 @@ This document maps every `InvoiceError`, `ContractError`, `SettlementError`, and ## InvoiceError -Defined in `contracts/invoice/src/lib.rs`. The `COMEBACKHERE-contracts/contracts/invoice` crate declares a related enum called `ContractError (invoice)` (see below) — variant names overlap but numeric codes are independent. +Historical error codes from the removed legacy invoice contract. The canonical `COMEBACKHERE-contracts/contracts/invoice` crate declares `ContractError (invoice)` below; variant names may overlap, but numeric codes are independent. | Code | Name | Trigger condition | Remediation | | ------ | ------ | ------------------- | ------------- | @@ -97,7 +97,7 @@ Both batch operations cap `addresses` at 50 entries per call (`ContractError::Ba ## SettlementError -Defined in `contracts/settlement/src/lib.rs`. +Historical error codes from the removed legacy settlement contract. Settlement operations are now handled by the canonical treasury contract. | Code | Name | Trigger condition | Remediation | | ------ | ------ | ------------------- | ------------- | diff --git a/docs/glossary.md b/docs/glossary.md index 8abe5b2..e2423af 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -187,7 +187,7 @@ A committed JSON file in the `abis/` directory that captures the contract's curr The automatic placement of a settlement into `OnHold` status when a `raise_dispute` call is made against it. Until the dispute is resolved (and the claimant or counterparty achieves the `resolution_weight` threshold), the settlement cannot be executed. **Mirrored Tree** -The dual-source-tree pattern where the same contract source code exists in both `COMEBACKHERE-contracts/contracts//` and top-level `contracts//` directories. The `COMEBACKHERE-` prefix tree is canonical and CI-tested; the top-level tree is deprecated but still referenced in some documentation. See `docs/ARCHITECTURE.md` for migration status. +An older in-repository copy of a canonical source tree. The former contract mirror has been removed; the backend and frontend mirrors remain during their migrations. See `ARCHITECTURE.md` for current source-of-truth paths. **Signer Weight** The numeric weight assigned to each signer in the treasury via `add_signer` or `update_signer_weight`. When a signer approves a settlement or votes on a dispute, their weight contributes to the cumulative approval weight. A signer with weight 0 is considered inactive. See also _Threshold_. diff --git a/scripts/check_error_docs_sync.sh b/scripts/check_error_docs_sync.sh index 44dafe0..1dde559 100755 --- a/scripts/check_error_docs_sync.sh +++ b/scripts/check_error_docs_sync.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # scripts/check_error_docs_sync.sh # -# Verify that every error variant declared in `contracts/` and -# `COMEBACKHERE-contracts/` is documented in `docs/error-codes.md`. +# Verify that every error variant declared in the canonical contract workspace +# is documented in `docs/error-codes.md`. # # This is a deliberately small grep/regex-based check; it intentionally # avoids a full Rust AST parser. It emits one CI failure category: a @@ -53,8 +53,6 @@ fi # 2. Scan contract sources for enum/variant tuples. # ----------------------------------------------------------------------------- CODE_DIRS=( - "$ROOT/contracts/invoice/src" - "$ROOT/contracts/settlement/src" "$ROOT/COMEBACKHERE-contracts/contracts/invoice/src" "$ROOT/COMEBACKHERE-contracts/contracts/treasury/src" "$ROOT/COMEBACKHERE-contracts/contracts/compliance/src"