├── .devcontainer.json ├── .envrc ├── .github └── workflows │ └── main.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── clippy.toml ├── contracts ├── cosmwasm │ ├── README.md │ ├── executor │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── authenticate.rs │ │ │ ├── bin │ │ │ └── executor.rs │ │ │ ├── contract.rs │ │ │ ├── contract_name.txt │ │ │ ├── error.rs │ │ │ ├── events.rs │ │ │ ├── lib.rs │ │ │ ├── msg.rs │ │ │ ├── prelude.rs │ │ │ └── state.rs │ ├── order │ │ ├── .devenv │ │ │ └── profile │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── bin │ │ │ └── order.rs │ │ │ ├── constants.rs │ │ │ ├── errors.rs │ │ │ ├── events.rs │ │ │ ├── lib.rs │ │ │ ├── prelude.rs │ │ │ ├── simulator.rs │ │ │ ├── state.rs │ │ │ ├── types.rs │ │ │ └── validation.rs │ ├── outpost │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── analyzer.rs │ │ │ ├── assets.rs │ │ │ ├── auth.rs │ │ │ ├── batch.rs │ │ │ ├── contract │ │ │ ├── contract_name.txt │ │ │ ├── execute.rs │ │ │ ├── ibc │ │ │ │ ├── ics20.rs │ │ │ │ ├── ics27.rs │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── query.rs │ │ │ ├── reply.rs │ │ │ └── sudo.rs │ │ │ ├── error.rs │ │ │ ├── events.rs │ │ │ ├── executor.rs │ │ │ ├── lib.rs │ │ │ ├── prelude.rs │ │ │ ├── router.rs │ │ │ └── state │ │ │ ├── assets.rs │ │ │ ├── exchange.rs │ │ │ ├── executors.rs │ │ │ ├── ics27.rs │ │ │ ├── mod.rs │ │ │ ├── network.rs │ │ │ └── tracking.rs │ ├── package-lock.json │ ├── package.json │ ├── schema │ │ ├── cvm-runtime.json │ │ └── raw │ │ │ ├── execute.json │ │ │ ├── instantiate.json │ │ │ ├── query.json │ │ │ ├── response_to_get_all_asset_ids.json │ │ │ ├── response_to_get_all_asset_venues.json │ │ │ ├── response_to_get_asset_by_id.json │ │ │ ├── response_to_get_config.json │ │ │ ├── response_to_get_exchange_by_id.json │ │ │ ├── response_to_get_ibc_ics20_route.json │ │ │ └── response_to_get_local_asset_by_reference.json │ ├── settlement │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ ├── tsconfig.json │ └── xcm │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ └── lib.rs ├── evm │ ├── .devenv │ │ └── profile │ ├── .gitignore │ ├── .prettierrc.json │ ├── .solhint.json │ ├── README │ ├── flake-module.nix │ ├── foundry.toml │ ├── hardhat.config.ts │ ├── hardhat_tests │ │ └── executor-tests.ts │ ├── package.json │ ├── project.json │ ├── script │ │ └── Counter.s.sol │ ├── src │ │ ├── Executor.sol │ │ ├── IbcBridge.sol │ │ ├── Router.sol │ │ ├── interfaces │ │ │ ├── IExecutor.sol │ │ │ ├── IIbcBridge.sol │ │ │ └── IRouter.sol │ │ ├── libraries │ │ │ ├── SDK.sol │ │ │ └── xc │ │ │ │ ├── README.md │ │ │ │ ├── common.sol │ │ │ │ └── xcvm.sol │ │ └── mocks │ │ │ ├── ERC20Mock.sol │ │ │ ├── IbcBridgeMock.sol │ │ │ └── SDKMock.sol │ ├── test │ │ ├── Executor..t.sol │ │ ├── IBCBridge.t.sol │ │ └── Router.t.sol │ ├── utils │ │ └── util.sol │ └── workspace.json └── solana │ ├── README.md │ ├── executor │ └── README.md │ └── outpost │ └── README.md ├── crates ├── cvm-glt │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── cvm-route │ ├── Cargo.toml │ └── src │ │ ├── asset.rs │ │ ├── exchange.rs │ │ ├── lib.rs │ │ ├── prelude.rs │ │ ├── primitive_types.rs │ │ ├── solana_program.rs │ │ ├── transport.rs │ │ └── venue.rs ├── cvm-runtime-exchange │ ├── Cargo.toml │ └── src │ │ ├── error.rs │ │ ├── lib.rs │ │ └── osmosis_std │ │ ├── mod.rs │ │ └── types │ │ ├── mod.rs │ │ └── osmosis │ │ ├── mod.rs │ │ └── poolmanager │ │ ├── mod.rs │ │ └── v1beta1.rs ├── cvm-runtime │ ├── .devenv │ │ └── profile │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ ├── SolidityTypes.proto │ │ ├── common.proto │ │ └── program.proto │ └── src │ │ ├── bin │ │ └── outpost.rs │ │ ├── bridge.rs │ │ ├── cosmwasm │ │ └── mod.rs │ │ ├── executor.rs │ │ ├── instruction.rs │ │ ├── lib.rs │ │ ├── outpost │ │ ├── config.rs │ │ ├── mod.rs │ │ └── query.rs │ │ ├── packet.rs │ │ ├── prelude.rs │ │ ├── program.rs │ │ ├── proto.rs │ │ ├── proto │ │ ├── cvm.rs │ │ ├── pb.rs │ │ └── result.rs │ │ ├── protocol.rs │ │ ├── shared.rs │ │ └── transport │ │ ├── ibc │ │ └── mod.rs │ │ ├── mod.rs │ │ └── xcm │ │ └── mod.rs ├── cvm │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── proto │ │ └── common.proto │ └── src │ │ ├── address.rs │ │ ├── asset.rs │ │ ├── exchange.rs │ │ ├── lib.rs │ │ ├── network.rs │ │ ├── prelude.rs │ │ ├── proto.rs │ │ └── shared.rs └── mantis-cw │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── lib.rs │ ├── ordered_coin_pair.rs │ └── ordered_tuple.rs ├── docs ├── Constant fees handling in optimal assets venues routing.md ├── Cross Domain Auctioner Core Architecture.md ├── MANTIS CDA security via Composable Restaking.md ├── MANTIS Solvers and Composable Restaking.md ├── cda │ └── orderflow-searchers-builders.puml ├── cvm │ ├── account-abstraction.md │ ├── lowering-routing.md │ └── specification.md ├── mantis-blackbox-architecture.md └── order │ ├── flow.md │ └── simple-mathemantics.md ├── flake.lock ├── flake.nix ├── mantis ├── .devcontainer.json ├── README.md ├── __init__.py ├── blackbox │ ├── README.md │ ├── __init__.py │ ├── composablefi_networks.py │ ├── custom_logging.py │ ├── cvm_indexer.py │ ├── cvm_runtime │ │ ├── __init__.py │ │ ├── execute.py │ │ ├── instantiate.py │ │ ├── query.py │ │ ├── response_to_get_all_asset_ids.py │ │ ├── response_to_get_all_asset_venues.py │ │ ├── response_to_get_asset_by_id.py │ │ ├── response_to_get_config.py │ │ ├── response_to_get_exchange_by_id.py │ │ ├── response_to_get_ibc_ics20_route.py │ │ └── response_to_get_local_asset_by_reference.py │ ├── example.json │ ├── logging_config.json │ ├── main.py │ ├── neutron_pools.py │ ├── osmosis_pools.py │ ├── raw.py │ ├── settings.py │ ├── skip_money.py │ └── test_joiner.py ├── blackbox_rs │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── check.sh ├── executor │ └── README.md ├── fix.sh ├── node │ ├── Cargo.lock │ ├── Cargo.toml │ ├── gen.sh │ ├── src │ │ ├── README.md │ │ ├── bin │ │ │ ├── mantis.rs │ │ │ └── simulator.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── mantis │ │ │ ├── args.rs │ │ │ ├── autopilot.rs │ │ │ ├── blackbox │ │ │ │ └── mod.rs │ │ │ ├── cosmos │ │ │ │ ├── client.rs │ │ │ │ ├── cosmwasm.rs │ │ │ │ ├── cvm.rs │ │ │ │ ├── mod.rs │ │ │ │ └── signer.rs │ │ │ ├── indexer.rs │ │ │ ├── mod.rs │ │ │ ├── simulate.rs │ │ │ └── solve.rs │ │ ├── prelude.rs │ │ └── solver │ │ │ ├── cows │ │ │ ├── mod.rs │ │ │ ├── optimizer.rs │ │ │ ├── orderbook.rs │ │ │ └── solution.rs │ │ │ ├── mod.rs │ │ │ ├── router │ │ │ ├── mod.rs │ │ │ ├── optimal_routing.rs │ │ │ ├── or.rs │ │ │ └── shortest_path.rs │ │ │ ├── simulator │ │ │ └── cfmm.rs │ │ │ └── types.rs │ └── tests │ │ ├── cows.rs │ │ ├── cvms.rs │ │ └── mod.rs ├── poetry.lock ├── pyproject.toml ├── simulation │ ├── .style.yapf │ ├── README.md │ ├── __init__.py │ ├── orders │ │ ├── README.md │ │ ├── __init__.py │ │ ├── matching.py │ │ ├── objects.py │ │ └── types.py │ └── routers │ │ ├── README.md │ │ ├── __init__.py │ │ ├── angeris_cvxpy │ │ ├── __init__._py │ │ ├── algorithms.py │ │ └── data.py │ │ ├── data.py │ │ ├── data │ │ ├── assets_pairs_xyk.csv │ │ └── assets_transfers.csv │ │ ├── errors.py │ │ ├── fixed-costs-etas-problem.md │ │ ├── generic_linear._py │ │ ├── oracles │ │ ├── __init__.py │ │ ├── bforacle.py │ │ ├── test_bforacle.py │ │ └── usdoracle.py │ │ ├── scaler.py │ │ ├── test_compare.py │ │ ├── test_data.py │ │ ├── test_data_types.py │ │ ├── test_generic_linear._py │ │ ├── test_production.py │ │ ├── test_scaler.py │ │ └── venue.py └── terraform │ ├── .gitignore │ ├── README.md │ ├── destroy.sh │ ├── docs.png │ ├── main.tf │ ├── mantis_app.yaml │ ├── output.tf │ ├── provider.tf │ ├── provision.sh │ └── variables.tf ├── rust-toolchain.toml └── schema ├── composablefi_networks.json ├── cvm-runtime.json ├── mantis_solver_blackbox.json ├── neutron_pools.json ├── osmosis_pools.json ├── raw ├── execute.json ├── instantiate.json ├── query.json ├── response_to_get_all_asset_ids.json ├── response_to_get_all_asset_venues.json ├── response_to_get_asset_by_id.json ├── response_to_get_config.json ├── response_to_get_exchange_by_id.json ├── response_to_get_ibc_ics20_route.json └── response_to_get_local_asset_by_reference.json ├── skip_money_assets.json ├── skip_money_chain.json └── skip_money_swagger.yml /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MANTIS", 3 | "hostRequirements": { 4 | "memory": "20gb", 5 | "cpus": 8 6 | }, 7 | "customizations": { 8 | "vscode": { 9 | "extensions": [ 10 | "mkhl.direnv", 11 | "GitHub.copilot", 12 | "bbenoist.Nix", 13 | "bierner.markdown-mermaid", 14 | "bpruitt-goddard.mermaid-markdown-syntax-highlighting", 15 | "humao.rest-client", 16 | "jebbs.plantuml", 17 | "yzhang.markdown-all-in-one", 18 | "ms-python.python", 19 | "ms-python.vscode-pylance" 20 | ], 21 | "settings": { 22 | "remote.autoForwardPorts": true, 23 | "workbench.iconTheme": "vscode-icons", 24 | "files.watcherExclude": { 25 | "**/target/**": true, 26 | "**/result/**": true 27 | } 28 | } 29 | } 30 | }, 31 | "portsAttributes": { 32 | "8000": { 33 | "label": "MANTIS Blackbox", 34 | "onAutoForward": "notify", 35 | "protocol": "http" 36 | } 37 | }, 38 | "remoteEnv": { 39 | "NIXPKGS_ALLOW_UNFREE": "1" 40 | }, 41 | "image": "ghcr.io/cachix/devenv:latest", 42 | "overrideCommand": false, 43 | "updateContentCommand": "nix flake update" 44 | } -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake . --impure -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "main" 2 | on: 3 | push: 4 | branches: 5 | - "main" 6 | pull_request: 7 | 8 | jobs: 9 | check: 10 | runs-on: "ubuntu-latest-m" 11 | if: github.event_name != 'push' 12 | concurrency: 13 | cancel-in-progress: true 14 | group: "${{ github.event_name }}-${{ github.ref_name }}-${{ github.ref_type }}--${{ github.ref }}-check" 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: cachix/install-nix-action@v20 18 | with: 19 | nix_path: nixpkgs=channel:nixos-unstable 20 | - uses: DeterminateSystems/magic-nix-cache-action@main 21 | - uses: cachix/cachix-action@v12 22 | with: 23 | authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 24 | name: composable 25 | skipPush: false 26 | - run: export NIXPKGS_ALLOW_UNFREE=1 && nix run .#ci --print-build-logs --verbose 27 | 28 | publish: 29 | if: github.event_name == 'push' 30 | runs-on: "ubuntu-latest-m" 31 | concurrency: 32 | cancel-in-progress: true 33 | group: "main" 34 | permissions: 35 | id-token: "write" 36 | contents: "read" 37 | steps: 38 | - uses: actions/checkout@v3 39 | - uses: cachix/install-nix-action@v20 40 | with: 41 | nix_path: nixpkgs=channel:nixos-unstable 42 | - uses: DeterminateSystems/magic-nix-cache-action@main 43 | - uses: cachix/cachix-action@v12 44 | with: 45 | authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 46 | name: composable 47 | skipPush: false 48 | - run: export NIXPKGS_ALLOW_UNFREE=1 && nix run .#ci 49 | - uses: "DeterminateSystems/flakehub-push@main" 50 | with: 51 | visibility: "public" 52 | rolling: true -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM arm64v8/ubuntu:latest 2 | 3 | # Update default packages 4 | RUN apt-get update 5 | 6 | # Get Ubuntu packages 7 | RUN apt-get install -y \ 8 | build-essential \ 9 | curl \ 10 | bash \ 11 | zip \ 12 | git \ 13 | libfl-dev \ 14 | clang \ 15 | coinor-cbc \ 16 | coinor-libcbc-dev 17 | 18 | 19 | # Get Rust 20 | RUN curl https://sh.rustup.rs -sSf | bash -s -- -y 21 | 22 | # Update new packages 23 | RUN apt-get update 24 | 25 | ENV PATH="/root/.cargo/bin:${PATH}" 26 | 27 | #Get Protobuf 28 | RUN curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v24.2/protoc-24.2-linux-x86_64.zip 29 | RUN unzip protoc-24.2-linux-x86_64.zip -d /usr/local/protoc 30 | ENV PATH=$PATH:/usr/local/protoc/bin 31 | RUN protoc --version 32 | 33 | 34 | # Copy the remaining files 35 | COPY . . 36 | 37 | # Build mantis 38 | RUN cargo build --bin mantis 39 | 40 | ENTRYPOINT ["/bin/bash"] 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Composable Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Composable Virtual Machine (CVM) 2 | 3 | The Composable Virtual Machine (CVM) is language and execution runtime for cross-chain program execution and intents settlement in [MANTIS](https://docs.composable.finance/technology/mantis). The CVM is an orchestration language on top of standards for cross-chain communication, specifically over IBC. 4 | 5 | ## [Specification](./docs/cvm/specification.md) 6 | 7 | ## [Composable Account Abstraction](./docs/cvm/account-abstraction.md) 8 | 9 | ## [Tutorial](https://docs.composable.finance/technology/cvm/tutorial) 10 | 11 | ## [Cross-Domain COWs & Constant Function MM Optimal Routing](https://github.com/ComposableFi/arxiv/blob/main/2024/01/Cross-Domain-COWs-%26-Constant-Function-MM-Routing-2024-Jan-30.pdf) 12 | 13 | For an isolated shell (virtual environment): 14 | 15 | ```sh 16 | nix develop 17 | ``` 18 | 19 | For `docs`: 20 | 21 | https://github.com/ComposableFi/composable-vm/tree/main/docs/ 22 | 23 | For production scripts: 24 | 25 | https://github.com/ComposableFi/env/ 26 | 27 | --- 28 | tags: "#mev #composable #cvm #mantis #defi #cosmos #cosmwasm #evm #ibc" 29 | --- 30 | 31 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | disallowed-types = [ 2 | # { path = "usize", reason = "variable size" }, # cannot on now, because serde, even if there is no usize in type 3 | { path = "f64", resdason = "harware dependant" }, 4 | { path = "f32", reason = "harware dependant" }, 5 | { path = "num_traits::float::*", reason = "harware dependant" }, 6 | { path = "serde_json::*", reason = "use serde_json_wasm::*" }, 7 | ] 8 | 9 | disallowed-methods = ["std::time::Duration::as_secs_f64"] 10 | -------------------------------------------------------------------------------- /contracts/cosmwasm/README.md: -------------------------------------------------------------------------------- 1 | 2 | `Item` pattern is used to store key within value to simplify code by price of storing more. 3 | 4 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "CVM Executor contract" 3 | edition = "2021" 4 | name = "cw-cvm-executor" 5 | repository = "https://github.com/ComposableFi/cvm" 6 | version = {workspace = true} 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | library = [] 12 | json-schema = [ 13 | "ibc-apps-more/json-schema", 14 | "cvm-runtime/json-schema", 15 | "cvm-route/json-schema", 16 | "cvm-runtime-exchange/json-schema", 17 | ] 18 | std = [ 19 | "cvm-runtime/std", 20 | "cvm-route/std", 21 | "dep:cosmwasm-schema", 22 | "ibc-apps-more/std", 23 | "thiserror/std", 24 | "cvm-runtime-exchange/std", 25 | ] 26 | cosmos = [ 27 | "cvm-runtime/cosmos", 28 | "cvm-route/cosmos", 29 | "cvm-runtime-exchange/cosmos", 30 | ] 31 | 32 | default = ["std", "cosmos", 33 | 34 | "cvm-runtime-exchange/cosmwasm", 35 | "cvm-runtime/cosmwasm", 36 | "cvm-route/cosmwasm", 37 | 38 | ] 39 | 40 | [dependencies] 41 | cvm-runtime-exchange ={ workspace = true, default-features = false } 42 | cosmwasm-std = { workspace = true, features = [ 43 | ], default-features = false} 44 | cw-storage-plus = { workspace = true } 45 | cosmwasm-schema = { workspace = true, optional = true } 46 | cw-utils = { workspace = true } 47 | cw2 = { workspace = true } 48 | cw20 = { workspace = true } 49 | hex = { workspace = true, default-features = false, features = ["alloc"] } 50 | schemars = { workspace = true } 51 | serde = { workspace = true } 52 | serde-json-wasm = { workspace = true } 53 | serde-cw-value = { workspace = true } 54 | thiserror = { workspace = true } 55 | prost = { workspace = true, features = ["prost-derive"] } 56 | cvm-runtime = { path = "../../../crates/cvm-runtime", features = [ 57 | "cosmwasm", 58 | "cosmos", 59 | ], default-features = false } 60 | num-traits = { workspace = true } 61 | 62 | cvm-route = { path = "../../../crates/cvm-route", features = [ 63 | "cosmwasm", 64 | ], default-features = false } 65 | 66 | 67 | ibc-apps-more = { workspace = true, default-features = false, features = [ 68 | "cosmwasm", 69 | ] } 70 | 71 | astroport = { workspace = true, default-features = false } 72 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/README.md: -------------------------------------------------------------------------------- 1 | # CVM Executor 2 | 3 | Receives and stores user funds. 4 | Fully owned by user. 5 | Delegates cross chain execution to `outpost`. 6 | 7 | Instantiated as many instances of the CVM executor contract. On some chains, we can use probabilistically generated sub_accounts, but for most, we instantiate a contract instance. 8 | 9 | ## Events 10 | 11 | Note that these events will be yield from the router in production. 12 | 13 | ### Instantiate contract 14 | ```json 15 | { 16 | "type": "wasm-cvm.executor.instantiated", 17 | "attributes": [ 18 | { 19 | "key": "data", 20 | "value": "{BASE64_ENCODED_DATA}" 21 | } 22 | ] 23 | } 24 | ``` 25 | 26 | - **BASE64_ENCODED_DATA**: base64 encoded `(network_id, user_id)` pair. 27 | 28 | ### Execute contract 29 | ```json 30 | { 31 | "type": "wasm-cvm.executor.executed", 32 | "attributes": [ 33 | { 34 | "key": "program", 35 | "value": "{CVM_PROGRAM_TAG}" 36 | } 37 | ] 38 | } 39 | ``` 40 | 41 | - **CVM_PROGRAM_TAG**: Tag of the executed CVM program 42 | 43 | ### Execute spawn instruction 44 | 45 | ```json 46 | { 47 | "type": "wasm-cvm.executor.spawn", 48 | "attributes": [ 49 | { 50 | "key": "origin_network_id", 51 | "value": "{ORIGIN_NETWORK_ID}" 52 | }, 53 | { 54 | "key": "origin_user_id", 55 | "value": "{ORIGIN_USER_ID}" 56 | }, 57 | { 58 | "key": "program", 59 | "value": "{CVM_PROGRAM}" 60 | } 61 | ] 62 | } 63 | ``` 64 | 65 | - **ORIGIN_NETWORK_ID**: Network id of the origin. Eg. Picasso, Ethereum 66 | - **ORIGIN_USER_ID**: Chain agnostic user identifier of the origin. Eg. contract_address in Juno 67 | - **CVM_PROGRAM**: Json-encoded cvm program. Note that although it is json-encoded, it is put as a string because of the restrictions of cosmwasm. 68 | 69 | ## Usage 70 | 71 | The CVM executor contract interprets the CVM programs. Available instructions are: 72 | 73 | 74 | ### Call 75 | Which is used to call a contract. See that the encoded payload must be in a format: 76 | ``` 77 | { 78 | "address": "contract-addr", 79 | "payload": "json-encoded ExecuteMsg struct" 80 | } 81 | ``` 82 | 83 | ### Transfer 84 | Queries `outpost`, gets the contract address and then executes that contract to do the transfer. 85 | 86 | ### Spawn 87 | Emits `spawn` event with the given parameters. 88 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/authenticate.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{ContractError, Result}, 3 | state::OWNERS, 4 | }; 5 | use cosmwasm_std::{Addr, Deps}; 6 | 7 | /// Authenticated token, MUST be private and kept in this module. 8 | /// MUST ONLY be instantiated by [`ensure_owner`]. 9 | pub struct Authenticated(()); 10 | 11 | /// Ensure that the caller is either the current executor or listed in the owners of the 12 | /// executor. 13 | /// Any operation executing against the executor must pass this check. 14 | pub fn ensure_owner(deps: Deps, self_addr: &Addr, sender: Addr) -> Result { 15 | if sender == self_addr || OWNERS.has(deps.storage, sender) { 16 | Ok(Authenticated(())) 17 | } else { 18 | Err(ContractError::NotAuthorized) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/bin/executor.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(feature = "json-schema", not(target_arch = "wasm32")))] 2 | #[allow(clippy::disallowed_methods)] 3 | fn main() { 4 | use cosmwasm_schema::write_api; 5 | use cvm_runtime::executor::*; 6 | use cw_cvm_executor::msg::*; 7 | 8 | write_api! { 9 | instantiate: InstantiateMsg, 10 | query: QueryMsg, 11 | execute: ExecuteMsg, 12 | } 13 | let events = schemars::gen::SchemaGenerator::default() 14 | .into_root_schema_for::(); 15 | 16 | // same as in above macro 17 | let mut out_dir = std::env::current_dir().unwrap(); 18 | out_dir.push("schema"); 19 | 20 | use ::std::fs::write; 21 | 22 | let path = out_dir.join(concat!("events", ".json")); 23 | 24 | write(path, serde_json_wasm::to_string(&events).unwrap()).unwrap(); 25 | } 26 | 27 | #[cfg(not(all(feature = "json-schema", not(target_arch = "wasm32"))))] 28 | fn main() {} 29 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/contract_name.txt: -------------------------------------------------------------------------------- 1 | composable_cvm_executor -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Response, StdError}; 2 | use cvm_runtime::LateBindingError; 3 | use thiserror::Error; 4 | 5 | pub type Result = core::result::Result; 6 | 7 | impl From for ContractError { 8 | fn from(_: cvm_runtime::ArithmeticError) -> Self { 9 | ContractError::InvalidProgram 10 | } 11 | } 12 | 13 | impl From> for ContractError { 14 | fn from(e: LateBindingError) -> Self { 15 | match e { 16 | LateBindingError::InvalidBinding => ContractError::InvalidBindings, 17 | LateBindingError::App(e) => e, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Error, Debug)] 23 | pub enum ContractError { 24 | #[error("{0}")] 25 | Std(#[from] StdError), 26 | 27 | #[error("{0}")] 28 | Exchange(#[from] cvm_runtime_exchange::error::ContractError), 29 | 30 | #[error("Invalid call payload")] 31 | InvalidCallPayload, 32 | 33 | #[error("Data cannot be serialized")] 34 | DataSerializationError, 35 | 36 | #[error("Program is invalid")] 37 | InvalidProgram, 38 | 39 | #[error("A program tag must be a correct utf8 encoded string")] 40 | InvalidProgramTag, 41 | 42 | #[error("Bindings are invalid")] 43 | InvalidBindings, 44 | 45 | #[error("Caller is not authorised to take the action")] 46 | NotAuthorized, 47 | 48 | #[error("Only the contract is authorized for this action")] 49 | NotSelf, 50 | 51 | #[error("Instruction {0} is not supported")] 52 | InstructionNotSupported(String), 53 | 54 | #[error("Address is invalid")] 55 | InvalidAddress, 56 | 57 | #[error("Unsupported")] 58 | Unsupported, 59 | 60 | #[error("An error occured while doing arithmetic operations")] 61 | ArithmeticError, 62 | 63 | #[error("Not implemented")] 64 | NotImplemented, 65 | 66 | #[error("The asset is not yet supported")] 67 | UnsupportedAsset, 68 | 69 | #[error("Only single asset exchange is supported by pool")] 70 | OnlySingleAssetExchangeIsSupportedByPool, 71 | 72 | /// for the case when specific pool does not supports slippage 73 | #[error("Exchange does not support slippage")] 74 | ExchangeDoesNotSupportSlippage, 75 | 76 | #[error("Cannot define both slippage and limit at same time")] 77 | CannotDefineBothSlippageAndLimitAtSameTime, 78 | 79 | #[error("Asset not found: {0}")] 80 | AssetNotFound(StdError), 81 | #[error("Exchange not found: {0}")] 82 | ExchangeNotFound(StdError), 83 | 84 | #[error("Asset unsupported on this network")] 85 | AssetUnsupportedOnThisNetwork, 86 | } 87 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types,))] 2 | extern crate alloc; 3 | 4 | pub mod authenticate; 5 | pub mod contract; 6 | pub mod error; 7 | pub mod events; 8 | pub mod msg; 9 | mod prelude; 10 | pub mod state; 11 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/msg.rs: -------------------------------------------------------------------------------- 1 | use crate::{prelude::*, state, state::State}; 2 | use cvm_runtime::Register; 3 | 4 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 5 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 6 | #[serde(rename_all = "snake_case")] 7 | pub struct MigrateMsg { 8 | /// Owners to be added to the list of owners which acts more like a recovery in case all of the 9 | /// owners are erased accidentally 10 | pub owners: Vec, 11 | } 12 | 13 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 14 | #[cfg_attr( 15 | feature = "json-schema", 16 | derive(schemars::JsonSchema, cosmwasm_schema::QueryResponses) 17 | )] 18 | #[serde(rename_all = "snake_case")] 19 | pub enum QueryMsg { 20 | /// Get a specific register 21 | #[cfg_attr(feature = "json-schema", returns(QueryStateResponse))] 22 | Register(Register), 23 | /// dumps the whole state of executor 24 | #[cfg_attr(feature = "json-schema", returns(QueryStateResponse))] 25 | State(), 26 | } 27 | 28 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 29 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 30 | #[serde(rename_all = "snake_case")] 31 | pub struct QueryStateResponse { 32 | pub state: state::State, 33 | } 34 | 35 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 36 | #[serde(rename_all = "snake_case")] 37 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 38 | pub struct QueryExchangeResponse { 39 | pub state: State, 40 | } 41 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use alloc::{string::String, vec::Vec}; 2 | pub use cosmwasm_std::Addr; 3 | pub use serde::{Deserialize, Serialize}; 4 | -------------------------------------------------------------------------------- /contracts/cosmwasm/executor/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{ 2 | to_json_binary, Addr, Binary, Order, StdError, StdResult, Storage, SubMsgResponse, 3 | }; 4 | use cvm_runtime::ExecutorOrigin; 5 | use cw_storage_plus::{Item, Map}; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 9 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 10 | pub struct Config { 11 | pub outpost_address: cvm_runtime::outpost::Outpost, 12 | pub executor_origin: ExecutorOrigin, 13 | } 14 | 15 | /// The executor configuration. 16 | pub const CONFIG: Item = Item::new("config"); 17 | 18 | /// List of owners able to execute programs on our behalf. Be aware that only `trusted` address must 19 | /// be added. 20 | pub const OWNERS: Map = Map::new("owners"); 21 | 22 | /// This register hold the latest program instruction (index) executed. 23 | pub const INSTRUCTION_POINTER_REGISTER: Item = Item::new("ip_register"); 24 | 25 | /// This register contains the latest executed instruction result for the program. 26 | /// It can be either a success `SubMsgResponse` or an error message (in this case changes of message 27 | /// were not applied). 28 | pub const RESULT_REGISTER: Item> = Item::new("result_register"); 29 | 30 | pub const TIP_REGISTER: Item = Item::new("tip_register"); 31 | 32 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 33 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 34 | pub struct State { 35 | pub result_register: Result, 36 | pub ip_register: u16, 37 | pub owners: Vec, 38 | pub config: Config, 39 | } 40 | 41 | impl TryInto for State { 42 | type Error = StdError; 43 | 44 | fn try_into(self) -> StdResult { 45 | to_json_binary(&self) 46 | } 47 | } 48 | 49 | pub(crate) fn read(storage: &dyn Storage) -> StdResult { 50 | Ok(State { 51 | result_register: RESULT_REGISTER.load(storage)?, 52 | ip_register: INSTRUCTION_POINTER_REGISTER.load(storage).unwrap_or(0), 53 | owners: OWNERS 54 | .range(storage, None, None, Order::Ascending) 55 | .map(|e| e.map(|(k, _)| k)) 56 | .collect::>>()?, 57 | config: CONFIG.load(storage)?, 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/.devenv/profile: -------------------------------------------------------------------------------- 1 | /nix/store/qhq84xind06z73abs3486szc61cj3d71-devenv-profile -------------------------------------------------------------------------------- /contracts/cosmwasm/order/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cw-mantis-order" 3 | version = {workspace = true} 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | 9 | [dependencies] 10 | cosmwasm-std = { workspace = true, features = [ 11 | "iterator", 12 | ], default-features = false } 13 | mantis-cw = { workspace = true, default-features = false } 14 | cw-controllers = { workspace = true, default-features = false } 15 | sylvia = { workspace = true, default-features = false } 16 | schemars = { workspace = true, default-features = false, optional = true } 17 | cosmwasm-schema = { workspace = true, default-features = false } 18 | serde = { workspace = true, default-features = false } 19 | cw-storage-plus = { workspace = true, features = [ 20 | "iterator", 21 | ], default-features = false } 22 | itertools = { workspace = true, features = [ 23 | "use_std", 24 | ], default-features = false } 25 | 26 | num-integer = { workspace = true, default-features = false, features = ["std"] } 27 | 28 | cvm-runtime = { path = "../../../crates/cvm-runtime", default-features = false, features = [ 29 | "cosmwasm", 30 | "std", 31 | ] } 32 | 33 | cvm-route = { path = "../../../crates/cvm-route", default-features = false, features = [ 34 | "cosmwasm", 35 | "std", 36 | ] } 37 | 38 | 39 | 40 | hex = {workspace = true, default-features = false, features = ["std"] } 41 | sha2 = {workspace = true, default-features = false, features = ["std"] } 42 | 43 | no-panic = { workspace = true } 44 | tuples = { workspace = true } 45 | num-rational = { workspace = true, default-features = false, features = [ 46 | "serde", 47 | ] } 48 | 49 | 50 | [features] 51 | std = [ 52 | "cvm-runtime/std", 53 | "num-rational/std", 54 | ] 55 | json-schema = [ 56 | "cvm-runtime/json-schema", "dep:schemars", 57 | "num-rational/json-schema" 58 | ] 59 | 60 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This contract implements `Order` instruction of CVM via (CoW)(https://en.wikipedia.org/wiki/Coincidence_of_wants). 4 | It integrates CoW with CFMM cross chain execution found by Solvers. 5 | 6 | ## General flow 7 | 8 | Some users request exchange some assets to other assets. 9 | Theirs tokens were transferred via well known bridges to here. 10 | 11 | User sends transactions containing order, describing amounts of assets they have(give) and want(take). 12 | Additionally they describe if they allow partial fill and timeout. 13 | Both assets must be local and registered in CVM. 14 | If target wanted out asset is just bridged, transfer path must be provided. 15 | 16 | Solvers read all user orders on chain, and propose solutions to do CoW amid orders (including order from solver which may come with solution), 17 | and cross chain CFMM for the rest. 18 | Each solver account has one and only one solution per pair. So solver can send and resend solution to replace old one for specific pair. 19 | 20 | Solution with maximum volume is accepted, and should settle on second largest volume (second bid auction). 21 | 22 | Bidders reserve amounts as rewards to pay for percentage of volume solved via their preferred CFMM. 23 | 24 | More details semantics will be described in whitepaper. including Solver operations. 25 | 26 | ## Solution 27 | 28 | In order solution have to be accepted, it must: 29 | 30 | - all CVM programs has salt to be solution id from hash of solver address, token a denom, token b denom and block solution was added 31 | - transfer all amounts as absolute as CVM instruction for cross chain part 32 | - all COWs solved against same price 33 | - none of user limits violated 34 | - only assets from orders inside solution are used 35 | - CVM must adhere Connection.fork_join_supported, for now it is always false (it restrict set routes possible) 36 | - Must adhere to single assets per spawn if multi assets are not supported (all for now) 37 | 38 | CoW cannot violate user limit, even if violating user limit allows to catch up better with CVM route. 39 | 40 | ## Implementation 41 | 42 | Current implementation is for 1 solution for one user, 1 to 1 asset, permissioned solvers without collateral. 43 | 44 | ### Bidding 45 | 46 | Bidder call this contract with CFMM identifier and percentage of volume they bid. 47 | This contract instantiates CVM executor per CFMM and transfer amount to it, and tracks percentage per bidder. 48 | When bidder joins, percentage averaged appropriately, with always larger bid for same volume. 49 | For each accepted route, Solver is delegated with relevant amount to withdraw. 50 | 51 | ## Data model 52 | 53 | Order has numeric incremental identifier per pair of tokens. 54 | 55 | Solution has owner per pair as identifier. 56 | 57 | If solution requires multiblock tracking for execution its id added with block solution was accepted. 58 | 59 | 60 | 61 | ## References 62 | 63 | https://github.com/openbook-dex/openbook-v2/blob/master/programs/openbook-v2/src/lib.rs -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/bin/order.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_arch = "wasm32"))] 2 | #[allow(clippy::disallowed_methods)] 3 | fn main() { 4 | use cosmwasm_schema::write_api; 5 | use cw_mantis_order::sv::*; 6 | 7 | write_api! { 8 | instantiate: InstantiateMsg, 9 | query: QueryMsg, 10 | execute: ExecMsg, 11 | } 12 | } 13 | 14 | #[cfg(target_arch = "wasm32")] 15 | fn main() {} 16 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/constants.rs: -------------------------------------------------------------------------------- 1 | /// each pair waits at least this amount of blocks before being decided 2 | pub const DEFAULT_BATCH_EPOCH: u32 = 2; 3 | 4 | /// count of solutions at minimum which can be decided, just set 1 for ease of devtest 5 | pub const DEFAULT_MIN_SOLUTION_COUNT: u32 = 1; 6 | 7 | pub const MAX_ORDER_COUNT: usize = 100; 8 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/errors.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Coin, StdError}; 2 | 3 | pub fn amount_does_not_decrease_want() -> StdError { 4 | StdError::generic_err("Amount does not decrease want") 5 | } 6 | 7 | pub fn expected_some_funds_in_order() -> StdError { 8 | StdError::generic_err("Expected some funds in order") 9 | } 10 | 11 | pub fn filled_order_cannot_be_cross_chain_routed() -> StdError { 12 | StdError::generic_err("Filled order cannot be cross chain routed") 13 | } 14 | 15 | pub fn partial_cross_chain_not_implemented() -> StdError { 16 | StdError::generic_err("Partial cross chain not implemented") 17 | } 18 | 19 | pub(crate) fn expected_some_funds_in_route() -> StdError { 20 | StdError::generic_err("Expected some funds in route") 21 | } 22 | 23 | pub fn max_order_count_reached(max: usize) -> StdError { 24 | StdError::generic_err(format!("Max order count reached: {}", max)) 25 | } 26 | 27 | pub fn banks_funds_must_be_at_least_routed(has: &Coin, expected: &Coin) -> StdError { 28 | StdError::generic_err(format!( 29 | "banks_funds_must_be_at_least_routed {:?} => {:?} ", 30 | has, expected 31 | )) 32 | } 33 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use cosmwasm_schema::cw_serde; 2 | pub use cosmwasm_std::{Addr, Coin}; 3 | pub use cosmwasm_std::{StdError, Uint128}; 4 | pub use tuples::*; 5 | 6 | pub use serde::{Deserialize, Serialize}; 7 | 8 | #[cfg(feature = "json-schema")] 9 | pub use cosmwasm_schema::schemars; 10 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/state.rs: -------------------------------------------------------------------------------- 1 | //! simple operation without constraint checks and calculations 2 | use cw_storage_plus::MultiIndex; 3 | use mantis_cw::Denom; 4 | 5 | use crate::*; 6 | 7 | /// tracks best price for A in pair, so that simplest solution which can take ALL orders can pay this one 8 | pub const PAIR_BEST_A: Map = Map::new("PAIR_BEST_A"); 9 | 10 | pub const PAIR_BEST_B: Map = Map::new("PAIR_BEST_B"); 11 | 12 | /// so we need to have several solution per pair to pick one best 13 | pub struct SolutionIndexes<'a> { 14 | /// (token pair secondary index), (stored item), (stored item full key) 15 | pub pair: MultiIndex<'a, DenomPair, SolutionItem, (DenomPair, SolverAddress)>, 16 | } 17 | 18 | /// (DenomPair,SolverAddress) -> SolutionItem 19 | pub type SolutionMultiMap<'a> = 20 | IndexedMap<'a, &'a (DenomPair, SolverAddress), SolutionItem, SolutionIndexes<'a>>; 21 | 22 | impl<'a> IndexList for SolutionIndexes<'a> { 23 | fn get_indexes(&'_ self) -> Box> + '_> { 24 | let v: Vec<&dyn Index> = vec![&self.pair]; 25 | Box::new(v.into_iter()) 26 | } 27 | } 28 | 29 | pub fn solutions<'a>() -> SolutionMultiMap<'a> { 30 | let indexes = SolutionIndexes { 31 | pair: MultiIndex::new( 32 | |_pk: &[u8], d: &SolutionItem| d.pair.clone(), 33 | "pair_solver_address", 34 | "pair", 35 | ), 36 | }; 37 | IndexedMap::new("solutions", indexes) 38 | } 39 | 40 | pub fn join_solution_with_orders( 41 | orders: &Map<'_, u128, OrderItem>, 42 | msg: &SolutionSubMsg, 43 | ctx: &ExecCtx<'_>, 44 | ) -> Result, StdError> { 45 | let all_orders = msg 46 | .cows 47 | .iter() 48 | .map(|x| { 49 | orders 50 | .load(ctx.deps.storage, x.order_id.u128()) 51 | .map_err(|_| StdError::not_found("order")) 52 | .and_then(|order| SolvedOrder::new(order, x.clone())) 53 | }) 54 | .collect::, _>>()?; 55 | Ok(all_orders) 56 | } 57 | 58 | pub fn get_solutions( 59 | solutions: &SolutionMultiMap, 60 | storage: &dyn Storage, 61 | ) -> Result, StdError> { 62 | solutions 63 | .idx 64 | .pair 65 | .range(storage, None, None, Order::Ascending) 66 | .map(|r| r.map(|(_, x)| x)) 67 | .collect() 68 | } 69 | -------------------------------------------------------------------------------- /contracts/cosmwasm/order/src/validation.rs: -------------------------------------------------------------------------------- 1 | //! AAA, collateral(stake)<->order, CVM route validation 2 | 3 | use cosmwasm_std::{Addr, StdResult}; 4 | 5 | use crate::{OrderItem, SolvedOrder}; 6 | 7 | /// Validate solver can solve order he tells. 8 | /// Minimal requirement is that CVM salt is unique to solver 9 | pub fn validate_solver( 10 | _as_ref: cosmwasm_std::Deps<'_>, 11 | _sender: &Addr, 12 | _order: &OrderItem, 13 | ) -> StdResult<()> { 14 | Ok(()) 15 | } 16 | 17 | /// Validate solver can solver amount he claimed 18 | pub(crate) fn validate_solvers( 19 | _deps: &cosmwasm_std::DepsMut<'_>, 20 | _solution: &crate::SolutionItem, 21 | _all_orders: &[SolvedOrder], 22 | ) -> StdResult<()> { 23 | Ok(()) 24 | } 25 | 26 | /// Validate solver program is sane 27 | /// Minimal requirement is that CVM salt is unique to solver 28 | pub(crate) fn validate_routes( 29 | _deps: &cosmwasm_std::DepsMut<'_>, 30 | _solution: &crate::SolutionItem, 31 | _all_orders: &[SolvedOrder], 32 | ) -> StdResult<()> { 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "CVM Outpost contract" 3 | edition = "2021" 4 | name = "cw-cvm-outpost" 5 | repository = "https://github.com/ComposableFi/cvm" 6 | version = {workspace = true} 7 | 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [lints] 12 | workspace = true 13 | 14 | [features] 15 | library = [] 16 | std = [ 17 | "cvm-runtime/std", 18 | "cvm-route/std", 19 | "cvm/std", 20 | "ibc-apps-more/std", 21 | "ibc-app-transfer-types/std", 22 | "ibc-core-host-types/std", 23 | "ibc-primitives/std", 24 | "thiserror/std", 25 | ] 26 | json-schema = [ 27 | "ibc-apps-more/json-schema", 28 | "ibc-app-transfer-types/schema", 29 | "ibc-core-host-types/schema", 30 | "ibc-primitives/schema", 31 | "cvm-runtime/json-schema", 32 | "cvm-route/json-schema", 33 | "cvm/json-schema", 34 | "dep:schemars", 35 | ] 36 | cosmos = [ 37 | "cvm-runtime/cosmos", 38 | "cvm-route/cosmos", 39 | "cvm/cosmos", 40 | ] 41 | default = ["std", "cosmos"] 42 | 43 | [dependencies] 44 | enumn = { workspace = true } 45 | bech32 = { workspace = true, features = [] } 46 | cosmwasm-std = { workspace = true, features = [ 47 | "ibc3", 48 | "stargate", 49 | "cosmwasm_1_1", 50 | "cosmwasm_1_2", 51 | ] } 52 | cw-storage-plus = { workspace = true } 53 | cw2 = { workspace = true } 54 | cw20 = { workspace = true } 55 | schemars = { workspace = true, optional = true} 56 | serde = { workspace = true } 57 | serde-json-wasm = { workspace = true } 58 | thiserror = { workspace = true } 59 | cvm-runtime = { path = "../../../crates/cvm-runtime", features = [ 60 | "cosmwasm", 61 | ], default-features = false } 62 | cvm = { path = "../../../crates/cvm", features = [ 63 | "cosmwasm", 64 | "cw-storage-plus", 65 | ], default-features = false } 66 | cvm-route = { path = "../../../crates/cvm-route", features = [ 67 | "cosmwasm", 68 | ], default-features = false } 69 | prost = { workspace = true, default-features = false } 70 | ibc-apps-more = { workspace = true, default-features = false, features = [ 71 | "cosmwasm", 72 | ] } 73 | 74 | ibc-app-transfer-types = { workspace = true, default-features = false, features = ["serde"] } 75 | ibc-core-host-types = { workspace = true, default-features = false, features = ["serde"] } 76 | ibc-primitives = { workspace = true, default-features = false, features = [ 77 | "serde", 78 | ] } -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/README.md: -------------------------------------------------------------------------------- 1 | # CVM Outpost Contract 2 | 3 | The CVM Outpost Contract is acting as bridge registry and default IBC bridge. 4 | 5 | Hides custom entry points and messaging under unified security, routing, assets handling and execution interface. 6 | 7 | Is Entrypoint and exit for Programs. 8 | Book keeps per-chain state. Holds the address mappings for cross-chain accounts and instantiates new executors.s -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/assets.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | auth, 3 | batch::BatchResponse, 4 | error::{ContractError, Result}, 5 | events::make_event, 6 | state::{ 7 | self, 8 | assets::{ASSETS, LOCAL_ASSETS}, 9 | }, 10 | }; 11 | use cosmwasm_std::{Deps, DepsMut}; 12 | use cvm_route::asset::{AssetItem, AssetReference, NetworkAssetItem}; 13 | use cvm_runtime::{AssetId, NetworkId}; 14 | 15 | /// Adds a new asset to the registry; errors out if asset already exists. 16 | pub(crate) fn force_asset(_: auth::Admin, deps: DepsMut, msg: AssetItem) -> Result { 17 | let config = crate::state::load(deps.storage)?; 18 | ASSETS.save(deps.storage, msg.asset_id, &msg)?; 19 | if msg.network_id == config.network_id { 20 | LOCAL_ASSETS.save(deps.storage, msg.local.clone(), &msg)?; 21 | } 22 | Ok(BatchResponse::new().add_event( 23 | make_event("assets.forced") 24 | .add_attribute("asset_id", msg.asset_id.to_string()) 25 | .add_attribute("denom", msg.denom()), 26 | )) 27 | } 28 | 29 | /// Fetches information about given asset. 30 | pub(crate) fn get_asset_by_id(deps: Deps, asset_id: AssetId) -> Result { 31 | ASSETS 32 | .may_load(deps.storage, asset_id)? 33 | .ok_or(ContractError::AssetIdNotFound(asset_id)) 34 | } 35 | 36 | /// Fetches information about given asset by its local reference. 37 | pub(crate) fn get_local_asset_by_reference( 38 | deps: Deps, 39 | reference: AssetReference, 40 | ) -> Result { 41 | LOCAL_ASSETS 42 | .may_load(deps.storage, reference.clone())? 43 | .ok_or(ContractError::AssetByReferenceNotFound(reference)) 44 | } 45 | 46 | /// Removes an existing asset from the registry; errors out if asset doesn’t 47 | /// exist. 48 | pub(crate) fn force_remove_asset( 49 | _: auth::Auth, 50 | deps: DepsMut<'_>, 51 | asset_id: AssetId, 52 | ) -> std::result::Result { 53 | let config = crate::state::load(deps.storage)?; 54 | let asset = ASSETS.load(deps.storage, asset_id)?; 55 | ASSETS.remove(deps.storage, asset_id); 56 | if asset.network_id == config.network_id { 57 | LOCAL_ASSETS.remove(deps.storage, asset.local); 58 | } 59 | Ok(BatchResponse::new() 60 | .add_event(make_event("assets.removed").add_attribute("asset_id", asset_id.to_string()))) 61 | } 62 | 63 | pub(crate) fn force_asset_to_network_map( 64 | _: auth::Admin, 65 | deps: DepsMut, 66 | this_asset: AssetId, 67 | other_network: NetworkId, 68 | other_asset: AssetId, 69 | ) -> Result { 70 | let item = NetworkAssetItem { 71 | from_asset_id: this_asset, 72 | to_network_id: other_network, 73 | to_asset_id: other_asset, 74 | }; 75 | state::assets::NETWORK_ASSET.save(deps.storage, (other_network, this_asset), &item)?; 76 | Ok(BatchResponse::new().add_event( 77 | make_event("assets.forced_asset_to_network_map") 78 | .add_attribute("this_asset", this_asset.to_string()) 79 | .add_attribute("other_asset", other_asset.to_string()), 80 | )) 81 | } 82 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/batch.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{CosmosMsg, Event, Response, SubMsg}; 2 | 3 | #[derive(Debug, Clone, Default)] 4 | pub struct BatchResponse { 5 | pub messages: Vec, 6 | pub events: Vec, 7 | } 8 | 9 | impl BatchResponse { 10 | pub fn new() -> Self { 11 | <_>::default() 12 | } 13 | pub fn add_message(mut self, msg: impl Into) -> Self { 14 | self.messages.push(SubMsg::new(msg)); 15 | self 16 | } 17 | 18 | pub fn add_submessage(mut self, msg: SubMsg) -> Self { 19 | self.messages.push(msg); 20 | self 21 | } 22 | 23 | pub fn add_event(mut self, event: Event) -> Self { 24 | self.events.push(event); 25 | self 26 | } 27 | 28 | pub fn merge(&mut self, mut other: Self) { 29 | self.messages.append(&mut other.messages); 30 | self.events.append(&mut other.events); 31 | } 32 | } 33 | 34 | impl From for Response { 35 | fn from(resp: BatchResponse) -> Self { 36 | let mut result = Self::new(); 37 | result.messages = resp.messages; 38 | result.events = resp.events; 39 | result 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/contract/contract_name.txt: -------------------------------------------------------------------------------- 1 | composable_cvm_executor -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/contract/ibc/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::events::make_event; 2 | 3 | pub mod ics20; 4 | pub mod ics27; 5 | 6 | pub fn make_ibc_failure_event(reason: String) -> cosmwasm_std::Event { 7 | make_event("receive") 8 | .add_attribute("result", "failure") 9 | .add_attribute("reason", reason) 10 | } 11 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/contract/query.rs: -------------------------------------------------------------------------------- 1 | use super::ibc::ics20::get_this_route; 2 | use crate::{assets, error::Result, msg}; 3 | use cosmwasm_std::{to_json_binary, Binary, Deps, Env}; 4 | 5 | #[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] 6 | pub fn query(deps: Deps, _env: Env, msg: msg::QueryMsg) -> Result { 7 | use msg::QueryMsg::*; 8 | match msg { 9 | GetAssetById { asset_id } => assets::get_asset_by_id(deps, asset_id) 10 | .and_then(|asset| Ok(to_json_binary(&msg::GetAssetResponse { asset })?)), 11 | GetLocalAssetByReference { reference } => { 12 | assets::get_local_asset_by_reference(deps, reference) 13 | .and_then(|asset| Ok(to_json_binary(&msg::GetAssetResponse { asset })?)) 14 | } 15 | GetIbcIcs20Route { 16 | to_network, 17 | for_asset, 18 | } => get_this_route(deps.storage, to_network, for_asset) 19 | .and_then(|route| Ok(to_json_binary(&msg::GetIbcIcs20RouteResponse { route })?)), 20 | GetExchangeById { exchange_id } => crate::state::exchange::get_by_id(deps, exchange_id) 21 | .and_then(|exchange| Ok(to_json_binary(&msg::GetExchangeResponse { exchange })?)), 22 | GetConfig {} => crate::state::get_config(deps).and_then(|x| Ok(to_json_binary(&x)?)), 23 | GetAllAssetIds {} => crate::state::assets::get_all_assets(deps) 24 | .and_then(|x| Ok(to_json_binary(&x)?)) 25 | .map_err(Into::into), 26 | GetAllAssetVenues {} => crate::state::exchange::get_all_exchange_venues(deps) 27 | .and_then(|x| Ok(to_json_binary(&x)?)) 28 | .map_err(Into::into), 29 | // GetRoute { program } => router::get_route(deps, program), 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/contract/reply.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | #[derive(PartialEq, Debug, Clone, Copy, EnumNum)] 4 | #[repr(u64)] 5 | pub enum ReplyId { 6 | InstantiateExecutor = 0, 7 | ExecProgram = 2, 8 | TransportSent = 3, 9 | } 10 | 11 | impl From for u64 { 12 | fn from(val: ReplyId) -> Self { 13 | val as u64 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/contract/sudo.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::ContractError, state}; 2 | use cosmwasm_std::{entry_point, wasm_execute, Coin, DepsMut, Env, Event, Response}; 3 | 4 | use ibc_apps_more::hook::{IBCLifecycleComplete, SudoMsg}; 5 | 6 | #[cfg_attr(not(feature = "library"), entry_point)] 7 | pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> crate::error::Result { 8 | deps.api.debug(&format!( 9 | "cvm::outpost::sudo {}", 10 | serde_json_wasm::to_string(&msg)? 11 | )); 12 | match msg { 13 | SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck { 14 | channel, 15 | sequence, 16 | ack, 17 | success, 18 | }) => { 19 | if !success { 20 | handle_transport_failure(deps, env, channel, sequence, ack) 21 | } else { 22 | Ok(Response::new()) 23 | } 24 | } 25 | SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCTimeout { channel, sequence }) => { 26 | handle_transport_failure(deps, env, channel, sequence, "timeout".to_string()) 27 | } 28 | } 29 | } 30 | 31 | /// return funds to executor and sets final error 32 | fn handle_transport_failure( 33 | deps: DepsMut, 34 | _env: Env, 35 | channel: ibc_core_host_types::identifiers::ChannelId, 36 | sequence: u64, 37 | reason: String, 38 | ) -> Result { 39 | deps.api.debug( 40 | format!( 41 | "cvm::outpost::handle::transport_failure {} {} {}", 42 | &channel, sequence, &reason 43 | ) 44 | .as_str(), 45 | ); 46 | let msg = cvm_runtime::executor::ExecuteMsg::SetErr { reason }; 47 | let bridge_msg = 48 | crate::state::tracking::get_execution_track(deps.storage, channel.as_str(), sequence)?; 49 | let executor = 50 | crate::state::executors::get_by_origin(deps.as_ref(), bridge_msg.executor_origin)?; 51 | let mut response = Response::new(); 52 | 53 | let assets = bridge_msg 54 | .msg 55 | .assets 56 | .into_iter() 57 | .filter_map(|(asset, amount)| { 58 | if let Ok(asset) = state::assets::ASSETS.load(deps.storage, asset) { 59 | Some(Coin { 60 | denom: asset.denom(), 61 | amount: amount.into(), 62 | }) 63 | } else { 64 | None 65 | } 66 | }) 67 | .collect(); 68 | 69 | response = response.add_message(wasm_execute(executor.address, &msg, assets)?); 70 | Ok(response.add_event(Event::new("cvm::outpost::handle::transport_failure"))) 71 | } 72 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/events.rs: -------------------------------------------------------------------------------- 1 | /// Creates an event with contract’s default prefix and given action attribute. 2 | pub(crate) fn make_event(action: &str) -> cosmwasm_std::Event { 3 | cosmwasm_std::Event::new("cvm.outpost").add_attribute("action", action) 4 | } 5 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] 2 | extern crate alloc; 3 | 4 | pub use cvm_runtime::outpost as msg; 5 | 6 | // pub mod analyzer; 7 | pub mod assets; 8 | pub mod auth; 9 | pub mod batch; 10 | pub mod contract; 11 | pub mod error; 12 | pub mod events; 13 | pub mod executor; 14 | mod prelude; 15 | //pub mod router; 16 | pub mod state; 17 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! mostly ensuring std vs no_std, and unified identifiers and numbers representation 2 | pub use alloc::format; 3 | pub use cosmwasm_std::Addr; 4 | pub use cvm_runtime::{outpost::config::*, shared::Displayed}; 5 | pub use cw_storage_plus::Map; 6 | pub use enumn::N as EnumNum; 7 | pub use ibc_core_host_types::identifiers::ChannelId; 8 | pub use serde::{Deserialize, Serialize}; 9 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/router.rs: -------------------------------------------------------------------------------- 1 | //! given program gives route information as far as it can 2 | use crate::prelude::*; 3 | use cvm_runtime::shared::XcProgram; 4 | 5 | pub(crate) fn get_route( 6 | deps: cosmwasm_std::Deps<'_>, 7 | program: XcProgram, 8 | ) -> Result { 9 | todo!() 10 | } 11 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/assets.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Deps, Order, StdResult}; 2 | use cvm_route::asset::{AssetItem, AssetReference, NetworkAssetItem}; 3 | use cvm_runtime::{AssetId, NetworkId}; 4 | 5 | use crate::prelude::*; 6 | 7 | pub type NetworkAssetId = (NetworkId, AssetId); 8 | 9 | /// when assets to be sent to other network it should be mapped before sent 10 | pub(crate) const NETWORK_ASSET: Map = Map::new("network_asset"); 11 | 12 | pub(crate) const ASSETS: Map = Map::new("assets"); 13 | pub(crate) const LOCAL_ASSETS: Map = Map::new("local_assets"); 14 | 15 | pub fn get_all_assets(deps: Deps) -> StdResult> { 16 | ASSETS 17 | .range(deps.storage, None, None, Order::Ascending) 18 | .map(|x| x.map(|(_, x)| x)) 19 | .collect() 20 | } 21 | 22 | pub fn get_all_network_assets(deps: Deps) -> StdResult> { 23 | NETWORK_ASSET 24 | .range(deps.storage, None, None, Order::Ascending) 25 | .map(|x| x.map(|(_, x)| x)) 26 | .collect() 27 | } 28 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/executors.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Deps, StdResult}; 2 | use cvm_runtime::ExecutorOrigin; 3 | use cw_storage_plus::Item; 4 | 5 | use crate::prelude::*; 6 | 7 | pub type ExecutorId = cvm_runtime::shared::Displayed; 8 | 9 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 10 | #[cfg_attr( 11 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 12 | derive(schemars::JsonSchema) 13 | )] 14 | pub(crate) struct ExecutorItem { 15 | /// contract address 16 | pub address: Addr, 17 | pub executor_id: ExecutorId, 18 | } 19 | 20 | pub(crate) fn get_by_origin(deps: Deps, origin: ExecutorOrigin) -> StdResult { 21 | let id = EXECUTOR_ORIGIN_TO_ID.load(deps.storage, origin)?; 22 | EXECUTORS.load(deps.storage, id) 23 | } 24 | 25 | pub(crate) const EXECUTORS_COUNT: Item = Item::new("executor_count"); 26 | 27 | pub(crate) const EXECUTOR_ORIGIN_TO_ID: Map = 28 | Map::new("executors_origin_to_id"); 29 | 30 | pub(crate) const EXECUTORS: Map = Map::new("executors"); 31 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/ics27.rs: -------------------------------------------------------------------------------- 1 | use cvm_route::transport::ChannelInfo; 2 | use cw_storage_plus::Map; 3 | 4 | use cvm_runtime::NetworkId; 5 | 6 | pub(crate) const IBC_CHANNEL_NETWORK: Map = Map::new("ibc_channel_network"); 7 | pub(crate) const IBC_CHANNEL_INFO: Map = Map::new("ibc_channel_info"); 8 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assets; 2 | pub mod exchange; 3 | pub mod executors; 4 | pub mod ics27; 5 | pub mod network; 6 | pub mod tracking; 7 | use crate::{error::ContractError, prelude::*}; 8 | 9 | use cosmwasm_std::{StdResult, Storage}; 10 | 11 | use cvm_route::asset; 12 | use cvm_runtime::outpost::GetConfigResponse; 13 | use cw_storage_plus::Item; 14 | 15 | const CONFIG: Item = Item::new("this"); 16 | 17 | pub(crate) fn load(storage: &dyn Storage) -> StdResult { 18 | CONFIG.load(storage) 19 | } 20 | 21 | pub(crate) fn save(storage: &mut dyn Storage, value: &HereItem) -> StdResult<()> { 22 | CONFIG.save(storage, value) 23 | } 24 | 25 | pub(crate) fn get_config(deps: cosmwasm_std::Deps<'_>) -> Result { 26 | use crate::state::*; 27 | let network_to_networks = network::get_all_network_to_network(deps)?; 28 | deps.api.debug("got networks to networks"); 29 | let exchanges = exchange::get_all_exchanges(deps)?; 30 | deps.api.debug("got exchanges"); 31 | let assets = assets::get_all_assets(deps)?; 32 | deps.api.debug("got assets"); 33 | let networks = network::get_all_networks(deps)?; 34 | deps.api.debug("got networks"); 35 | let network_assets = assets::get_all_network_assets(deps)?; 36 | deps.api.debug("got network assets"); 37 | let asset_venue_items = exchange::get_all_exchange_venues(deps)?; 38 | deps.api.debug("got asset venue items"); 39 | let get_config_response = GetConfigResponse { 40 | network_to_networks, 41 | assets, 42 | exchanges, 43 | networks, 44 | network_assets, 45 | asset_venue_items, 46 | }; 47 | Ok(get_config_response) 48 | } 49 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/network.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | batch::BatchResponse, events::make_event, prelude::*, state::ics27::IBC_CHANNEL_NETWORK, 3 | }; 4 | use cosmwasm_std::{Deps, DepsMut, Order, StdResult, Storage}; 5 | use cvm_route::transport::*; 6 | use cvm_runtime::{outpost::NetworkItem, NetworkId}; 7 | 8 | use crate::state::{self}; 9 | 10 | use crate::error::{ContractError, Result}; 11 | 12 | pub fn load_this(storage: &dyn Storage) -> Result { 13 | state::load(storage) 14 | .and_then(|this| NETWORK.load(storage, this.network_id)) 15 | .map_err(|_| ContractError::NetworkConfig) 16 | } 17 | 18 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 19 | #[serde(rename_all = "snake_case")] 20 | pub struct OtherNetwork { 21 | pub network: NetworkItem, 22 | pub connection: OtherNetworkItem, 23 | } 24 | 25 | pub fn load_other(storage: &dyn Storage, other: NetworkId) -> Result { 26 | let this = state::load(storage)?; 27 | let other: NetworkItem = NETWORK.load(storage, other)?; 28 | let connection: NetworkToNetworkItem = 29 | NETWORK_TO_NETWORK.load(storage, (this.network_id, other.network_id))?; 30 | Ok(OtherNetwork { 31 | network: other, 32 | connection: connection.to_network, 33 | }) 34 | } 35 | 36 | pub(crate) fn force_network_to_network( 37 | _: crate::auth::Auth, 38 | deps: DepsMut, 39 | msg: cvm_route::transport::NetworkToNetworkItem, 40 | ) -> std::result::Result { 41 | NETWORK_TO_NETWORK.save(deps.storage, (msg.from_network_id, msg.to_network_id), &msg)?; 42 | if let Some(ibc) = msg.to_network.ics27_channel { 43 | IBC_CHANNEL_NETWORK.save(deps.storage, ibc.id.to_string(), &msg.to_network_id)?; 44 | } 45 | Ok(BatchResponse::new().add_event( 46 | make_event("network_to_network.forced") 47 | .add_attribute("to", msg.to_network_id.to_string()) 48 | .add_attribute("from", msg.from_network_id.to_string()) 49 | .add_attribute("ics_20", msg.to_network.ics_20.is_some().to_string()), 50 | )) 51 | } 52 | 53 | pub(crate) fn force_network( 54 | _auth: crate::auth::Auth, 55 | deps: DepsMut, 56 | msg: NetworkItem, 57 | ) -> crate::error::Result { 58 | NETWORK.save(deps.storage, msg.network_id, &msg)?; 59 | Ok(BatchResponse::new().add_event( 60 | make_event("network.forced").add_attribute("network_id", msg.network_id.to_string()), 61 | )) 62 | } 63 | 64 | /// the connection description from first network to second 65 | pub(crate) const NETWORK_TO_NETWORK: Map<(NetworkId, NetworkId), NetworkToNetworkItem> = 66 | Map::new("network_to_network"); 67 | 68 | /// network state shared among all networks about it 69 | pub(crate) const NETWORK: Map = Map::new("network"); 70 | 71 | pub fn get_all_networks(deps: Deps) -> StdResult> { 72 | NETWORK 73 | .range(deps.storage, None, None, Order::Ascending) 74 | .map(|x| x.map(|(_, x)| x)) 75 | .collect() 76 | } 77 | 78 | pub fn get_all_network_to_network(deps: Deps) -> StdResult> { 79 | NETWORK_TO_NETWORK 80 | .range(deps.storage, None, None, Order::Ascending) 81 | .map(|x| x.map(|(_, x)| x)) 82 | .collect() 83 | } 84 | -------------------------------------------------------------------------------- /contracts/cosmwasm/outpost/src/state/tracking.rs: -------------------------------------------------------------------------------- 1 | //! is used to track cross chain programs execution statuses (non atomic execution) 2 | //! useful for timeouts, message acknowledgements failures, spawn concurrency handling 3 | 4 | use cosmwasm_std::{StdResult, Storage}; 5 | use cvm_runtime::{ 6 | outpost::BridgeForwardMsg, 7 | transport::ibc::{IbcIcs20ProgramRoute, TransportTrackerId}, 8 | }; 9 | use cw_storage_plus::{Item, Map}; 10 | 11 | pub(crate) const CURRENT_BRIDGE: Item<(BridgeForwardMsg, IbcIcs20ProgramRoute)> = 12 | Item::new("current_bridge"); 13 | 14 | pub(crate) const CHANNEL_SEQUENCE_TO_BRIDGE_MSG: Map<(String, u64), BridgeForwardMsg> = 15 | Map::new("channel_sequence_to_bridge_msg"); 16 | 17 | pub fn track( 18 | storage: &mut dyn Storage, 19 | tracker_id: TransportTrackerId, 20 | msg: BridgeForwardMsg, 21 | ) -> StdResult<()> { 22 | let (channel_id, sequence) = match tracker_id { 23 | TransportTrackerId::Ibc { 24 | channel_id, 25 | sequence, 26 | } => (channel_id, sequence), 27 | }; 28 | let key = (channel_id.to_string(), sequence); 29 | 30 | CHANNEL_SEQUENCE_TO_BRIDGE_MSG.save(storage, key, &msg) 31 | } 32 | 33 | pub fn bridge_lock( 34 | storage: &mut dyn Storage, 35 | lock: (BridgeForwardMsg, IbcIcs20ProgramRoute), 36 | ) -> StdResult<()> { 37 | if CURRENT_BRIDGE.load(storage).is_ok() { 38 | return Err(cosmwasm_std::StdError::GenericErr { 39 | msg: "bridge is locked".to_string(), 40 | }); 41 | } 42 | 43 | CURRENT_BRIDGE.save(storage, &lock)?; 44 | Ok(()) 45 | } 46 | 47 | pub fn bridge_unlock( 48 | storage: &mut dyn Storage, 49 | ) -> StdResult<(BridgeForwardMsg, IbcIcs20ProgramRoute)> { 50 | let item = CURRENT_BRIDGE.load(storage)?; 51 | CURRENT_BRIDGE.remove(storage); 52 | Ok(item) 53 | } 54 | 55 | /// Gets executor and gateway owned state on behalf executor (coins) 56 | pub fn get_execution_track( 57 | storage: &dyn Storage, 58 | channel_id: &str, 59 | sequence: u64, 60 | ) -> StdResult { 61 | let key = (channel_id.to_string(), sequence); 62 | let msg = CHANNEL_SEQUENCE_TO_BRIDGE_MSG.load(storage, key)?; 63 | Ok(msg) 64 | } 65 | -------------------------------------------------------------------------------- /contracts/cosmwasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cvm-cw-types", 3 | "version": "0.4.0", 4 | "description": "Composable VM CosmWasm types and client", 5 | "files": [ 6 | "dist/**/*" 7 | ], 8 | "scripts": { 9 | "build-cvm-runtime": "cosmwasm-ts-codegen generate --plugin client --schema ./schema --no-bundle --out dist/cw-cvm-runtime/ --name cw-cvm-runtime && cp --recursive schema/* dist/cw-cvm-runtime/ ", 10 | "build-cw-cvm-executor": "cosmwasm-ts-codegen generate --plugin client --schema ./schema --no-bundle --out dist/cw-cvm-executor/ --name cw-cvm-executor && cp --recursive schema/* dist/cw-cvm-executor/", 11 | "build-cw-mantis-order": "cosmwasm-ts-codegen generate --plugin client --schema ./schema --no-bundle --out ./dist/cw-mantis-order --name cw-mantis-order && cp --recursive schema/* dist/cw-mantis-order/" 12 | }, 13 | "type": "module", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@cosmjs/cosmwasm-stargate": "^0.32.3", 17 | "cosmjs-types": "^0.9.0" 18 | }, 19 | "devDependencies": { 20 | "@cosmwasm/ts-codegen": "^1.7.1" 21 | } 22 | } -------------------------------------------------------------------------------- /contracts/cosmwasm/schema/raw/instantiate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "allOf": [ 5 | { 6 | "$ref": "#/definitions/HereItem" 7 | } 8 | ], 9 | "definitions": { 10 | "Addr": { 11 | "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", 12 | "type": "string" 13 | }, 14 | "HereItem": { 15 | "type": "object", 16 | "required": [ 17 | "admin", 18 | "network_id" 19 | ], 20 | "properties": { 21 | "admin": { 22 | "description": "The admin which is allowed to update the bridge list.", 23 | "allOf": [ 24 | { 25 | "$ref": "#/definitions/Addr" 26 | } 27 | ] 28 | }, 29 | "network_id": { 30 | "description": "Network ID of this network where contract is deployed", 31 | "allOf": [ 32 | { 33 | "$ref": "#/definitions/NetworkId" 34 | } 35 | ] 36 | } 37 | } 38 | }, 39 | "NetworkId": { 40 | "description": "Newtype for CVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.", 41 | "type": "integer", 42 | "format": "uint32", 43 | "minimum": 0.0 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/cosmwasm/schema/raw/response_to_get_all_asset_venues.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Array_of_AssetsVenueItem", 4 | "type": "array", 5 | "items": { 6 | "$ref": "#/definitions/AssetsVenueItem" 7 | }, 8 | "definitions": { 9 | "AssetId": { 10 | "description": "Newtype for CVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.", 11 | "type": "string" 12 | }, 13 | "AssetsVenueItem": { 14 | "description": "assets which can be transomed into each other via venue", 15 | "type": "object", 16 | "required": [ 17 | "from_asset_id", 18 | "to_asset_id", 19 | "venue_id" 20 | ], 21 | "properties": { 22 | "from_asset_id": { 23 | "$ref": "#/definitions/AssetId" 24 | }, 25 | "to_asset_id": { 26 | "$ref": "#/definitions/AssetId" 27 | }, 28 | "venue_id": { 29 | "$ref": "#/definitions/VenueId" 30 | } 31 | } 32 | }, 33 | "Displayed_for_uint128": { 34 | "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n```rust use cvm::shared::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", 35 | "type": "string" 36 | }, 37 | "VenueId": { 38 | "oneOf": [ 39 | { 40 | "type": "string", 41 | "enum": [ 42 | "transfer" 43 | ] 44 | }, 45 | { 46 | "type": "object", 47 | "required": [ 48 | "exchange" 49 | ], 50 | "properties": { 51 | "exchange": { 52 | "$ref": "#/definitions/Displayed_for_uint128" 53 | } 54 | }, 55 | "additionalProperties": false 56 | } 57 | ] 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/cosmwasm/settlement/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "MANTIS Settlemnt contract" 3 | edition = "2021" 4 | name = "cw-mantis-settlemnt" 5 | version = {workspace = true} 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [lints] 11 | workspace = true -------------------------------------------------------------------------------- /contracts/cosmwasm/settlement/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Is used to move funds here to allow any kind of multiblock execution and later verification. 4 | 5 | 6 | ## CoW/Batch auctions 7 | 8 | https://github.com/sei-protocol/sei-chain/tree/main/x/dex 9 | 10 | 11 | https://docs.cow.fi/ -------------------------------------------------------------------------------- /contracts/cosmwasm/settlement/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /contracts/cosmwasm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ES2022", 5 | "moduleResolution": "NodeNext", 6 | "strict": true, 7 | } 8 | } -------------------------------------------------------------------------------- /contracts/cosmwasm/xcm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "CW XCM" 3 | edition = "2021" 4 | name = "cw-xcm" 5 | version = {workspace = true} 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [lints] 11 | workspace = true -------------------------------------------------------------------------------- /contracts/cosmwasm/xcm/README.md: -------------------------------------------------------------------------------- 1 | # Testbed to support XCM on Cosmos 2 | 3 | `executor` - corresponds to parity `xcm-executor` 4 | `configuraiton` - similar to usual configuration in substrate chain. Maps addresses, IBC connection/ports to and from XCM MultiLocation, barrier 5 | `transactor` - uses `xcm-configuration` and `cw20-ics20` to transact assets 6 | `trap-claim` - lost assets traps and claims 7 | `runtime` - governance of all contracts (like treasury) 8 | `simulator` helper tools to run `xcm` message amid several contracts on top of `cosmwasm-vm-test`. For each case there will be at least 1 positive test. 9 | `sender` - sends message to IBC 10 | -------------------------------------------------------------------------------- /contracts/cosmwasm/xcm/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /contracts/evm/.devenv/profile: -------------------------------------------------------------------------------- 1 | /nix/store/yj6rc7jpj9pww0ifvhrrwpghirai5c1y-devenv-profile -------------------------------------------------------------------------------- /contracts/evm/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | -------------------------------------------------------------------------------- /contracts/evm/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } -------------------------------------------------------------------------------- /contracts/evm/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": "error", 6 | "compiler-version": ["error", "^0.8.14"], 7 | "reason-string": ["off", { "maxLength": 32 }], 8 | "func-visibility": ["warn",{"ignoreConstructors":false}], 9 | "no-empty-blocks": ["off"], 10 | "not-rely-on-time": "off" 11 | } 12 | } -------------------------------------------------------------------------------- /contracts/evm/README: -------------------------------------------------------------------------------- 1 | # EVM 2 | 3 | 4 | EVM SDK uses `cvm-typescript-sdk` library for its tests. Whenever the library source code is edited, it should be updated here in evm directory by running `npm install --force` 5 | 6 | 7 | ## Run tests 8 | There are 2 set of tests 9 | - Router and bridge related tests, which are implemented in solidity using `forge` 10 | - `npm run test` or `forge test` 11 | - CVM program encoding and decoding related tests, which are implemented in typescript using `hardhat` 12 | - `npm run test-proto` 13 | 14 | -------------------------------------------------------------------------------- /contracts/evm/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | libs = ['lib'] 3 | out = 'out' 4 | remappings = [ 5 | 'forge-std/=lib/forge-std/src/', 6 | 'openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/', 7 | '@openzeppelin/=lib/openzeppelin-contracts/', 8 | 'protobuf3-solidity-lib/=lib/protobuf3-solidity-lib/contracts/', 9 | 'bytes-utils/=lib/solidity-bytes-utils/contracts/', 10 | 'yui-ibc/=lib/yui-ibc-solidity/contracts/', 11 | ] 12 | src = 'src' 13 | -------------------------------------------------------------------------------- /contracts/evm/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import "@nomiclabs/hardhat-waffle"; 3 | import "@typechain/hardhat"; 4 | import "hardhat-preprocessor"; 5 | import {HardhatUserConfig, task} from "hardhat/config"; 6 | 7 | function getRemappings() { 8 | return fs 9 | .readFileSync("hardhatRemappings.txt", "utf8") 10 | .split("\n") 11 | .filter(Boolean) 12 | .map((line) => line.trim().split("=")); 13 | } 14 | 15 | const config: HardhatUserConfig = { 16 | networks: { 17 | hardhat: { 18 | gas: 10000000, 19 | allowUnlimitedContractSize: true 20 | } 21 | }, 22 | solidity: { 23 | version: "0.8.14", 24 | settings: { 25 | optimizer: { 26 | enabled: true, 27 | runs: 200, 28 | }, 29 | }, 30 | }, 31 | paths: { 32 | sources: "./src", // Use ./src rather than ./contracts as Hardhat expects 33 | cache: "./cache_hardhat", // Use a different cache for Hardhat than Foundry 34 | tests: "./hardhat_tests", 35 | }, 36 | // This fully resolves paths for imports in the ./lib directory for Hardhat 37 | preprocess: { 38 | eachLine: (hre) => ({ 39 | transform: (line: string) => { 40 | if (line.match(/^\s*import /i)) { 41 | getRemappings().forEach(([find, replace]) => { 42 | if (line.match(find)) { 43 | line = line.replace(find, replace); 44 | } 45 | }); 46 | } 47 | return line; 48 | }, 49 | }), 50 | }, 51 | }; 52 | 53 | export default config; 54 | -------------------------------------------------------------------------------- /contracts/evm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xcvm", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "preinstall": "cd ../xcvm-typescript-sdk/ && npm install && npm run build", 7 | "test": "forge test -vvv", 8 | "coverage": "forge coverage", 9 | "solhint": "solhint src/**/*.sol", 10 | "proto": "protoc ../xcvm-typescript-sdk/src/executor.proto -o executor_proto.bin", 11 | "test-proto": "hardhat test", 12 | "compile": "hardhat compile" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@openzeppelin/hardhat-upgrades": "^1.19.0", 17 | "@prb/math": "^2.5.0", 18 | "ethers": "^5.6.6", 19 | "tslib": "^2.0.0" 20 | }, 21 | "devDependencies": { 22 | "@lazyledger/protobuf3-solidity-lib": "^0.6.0", 23 | "@nomiclabs/hardhat-ethers": "^2.0.6", 24 | "@nomiclabs/hardhat-etherscan": "^3.1.0", 25 | "@nomiclabs/hardhat-waffle": "^2.0.3", 26 | "@nrwl/cli": "14.1.9", 27 | "@nrwl/devkit": "14.1.9", 28 | "@nrwl/eslint-plugin-nx": "14.1.9", 29 | "@nrwl/linter": "14.1.9", 30 | "@nrwl/node": "^14.1.9", 31 | "@nrwl/workspace": "14.1.9", 32 | "@openzeppelin/contracts": "^4.6.0", 33 | "@openzeppelin/contracts-upgradeable": "^4.6.0", 34 | "@typechain/ethers-v5": "^10.1.0", 35 | "@typechain/hardhat": "^6.1.2", 36 | "@types/chai": "^4.3.0", 37 | "@types/mocha": "^9.1.0", 38 | "@types/node": "^17.0.35", 39 | "@typescript-eslint/eslint-plugin": "~5.18.0", 40 | "@typescript-eslint/parser": "~5.18.0", 41 | "chai": "^4.3.6", 42 | "eslint": "~8.12.0", 43 | "eslint-config-prettier": "8.1.0", 44 | "ethereum-waffle": "^3.2.0", 45 | "ethers": "^5.6.6", 46 | "hardhat": "2.9.7", 47 | "hardhat-preprocessor": "^0.1.5", 48 | "hardhat-tracer": "^1.1.0-rc.6", 49 | "nx": "14.1.9", 50 | "prettier": "^2.7.1", 51 | "prettier-plugin-solidity": "^1.0.0-beta.24", 52 | "protobufjs": "~6.10.1", 53 | "sol-merger": "^4.1.1", 54 | "solhint": "^3.3.7", 55 | "solhint-plugin-prettier": "^0.0.5", 56 | "solidity-coverage": "^0.7.21", 57 | "ts-node": "^10.7.0", 58 | "typechain": "^8.1.0", 59 | "typescript": "~4.6.2", 60 | "xcvm-typescript-sdk": "file:../xcvm-typescript-sdk" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/evm/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "./src", 4 | "projectType": "application", 5 | "targets": { 6 | "build": { 7 | "executor": "@nrwl/workspace:run-commands", 8 | "options": { 9 | "commands": [ 10 | { 11 | "command": "forge build --optimize" 12 | } 13 | ] 14 | } 15 | }, 16 | "test": { 17 | "executor": "@nrwl/workspace:run-commands", 18 | "options": { 19 | "commands": [ 20 | { 21 | "command": "forge test -vvv --gas-report" 22 | } 23 | ] 24 | } 25 | }, 26 | "testWatch": { 27 | "executor": "@nrwl/workspace:run-commands", 28 | "options": { 29 | "commands": [ 30 | { 31 | "command": "forge test -vvv --watch" 32 | } 33 | ] 34 | } 35 | }, 36 | "coverage": { 37 | "executor": "@nrwl/workspace:run-commands", 38 | "options": { 39 | "commands": [ 40 | { 41 | "command": "forge coverage" 42 | } 43 | ] 44 | } 45 | }, 46 | "tags": [] 47 | } 48 | -------------------------------------------------------------------------------- /contracts/evm/script/Counter.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.14; 3 | 4 | //import "forge-std/Script.sol"; 5 | 6 | //contract CounterScript is Script { 7 | // function setUp() public {} 8 | // 9 | // function run() public { 10 | // vm.broadcast(); 11 | // } 12 | //} 13 | -------------------------------------------------------------------------------- /contracts/evm/src/Executor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.14; 3 | 4 | import "protobuf3-solidity-lib/ProtobufLib.sol"; 5 | import "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | import "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 7 | import "./interfaces/IExecutor.sol"; 8 | import "./interfaces/IRouter.sol"; 9 | import "./libraries/SDK.sol"; 10 | 11 | /** 12 | * @title Executor 13 | * @notice Custom executor 14 | */ 15 | contract Executor is IExecutor { 16 | 17 | address public creator; 18 | address public routerAddress; 19 | bytes public override salt; 20 | mapping(address => bool) public owners; 21 | IRouter.Origin origin; 22 | 23 | modifier onlyOwnerOrCreator() { 24 | require(keccak256(abi.encodePacked(msg.sender)) == keccak256(origin.account) || owners[msg.sender] || address(this) == msg.sender); 25 | _; 26 | } 27 | 28 | constructor(IRouter.Origin memory _origin, address _routerAddress, bytes memory _salt) { 29 | owners[msg.sender] = true; 30 | creator = msg.sender; 31 | routerAddress = _routerAddress; 32 | salt = _salt; 33 | origin = _origin; 34 | } 35 | 36 | function addOwners(address[] memory newOwners) public onlyOwnerOrCreator { 37 | for(uint256 i=0; i=0.8.13; 3 | 4 | // Based on: https://github.com/FrankieIsLost/forge-template/blob/master/src/test/utils/Utilities.sol 5 | 6 | import "forge-std/Vm.sol"; 7 | import "ds-test/test.sol"; 8 | 9 | //common utilities for forge tests 10 | contract Utils { 11 | Vm vm; 12 | bytes32 internal nextUser = 13 | keccak256(abi.encodePacked("user address")); 14 | 15 | constructor(Vm newVm) { 16 | vm = newVm; 17 | } 18 | 19 | function getNextUserAddress() external returns (address payable) { 20 | //bytes32 to address conversion 21 | address payable user = payable( 22 | address(uint160(uint256(nextUser))) 23 | ); 24 | nextUser = keccak256(abi.encodePacked(nextUser)); 25 | return user; 26 | } 27 | 28 | //create users with 100 ether balance 29 | function createUsers(uint256 userNum) 30 | external 31 | returns (address payable[] memory) 32 | { 33 | address payable[] memory users = new address payable[]( 34 | userNum 35 | ); 36 | for (uint256 i = 0; i < userNum; i++) { 37 | address payable user = this.getNextUserAddress(); 38 | vm.deal(user, 100 ether); 39 | users[i] = user; 40 | } 41 | return users; 42 | } 43 | 44 | //move block.number forward by a given number of blocks 45 | function mineBlocks(uint256 numBlocks) external { 46 | uint256 targetBlock = block.number + numBlocks; 47 | vm.roll(targetBlock); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /contracts/evm/workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/workspace-schema.json", 3 | "version": 2, 4 | "projects": { 5 | "smart-contracts": "./" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /contracts/solana/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Describes solana-outpost and solana-executor contracts. 4 | 5 | -------------------------------------------------------------------------------- /contracts/solana/executor/README.md: -------------------------------------------------------------------------------- 1 | 2 | Per user program instance. -------------------------------------------------------------------------------- /contracts/solana/outpost/README.md: -------------------------------------------------------------------------------- 1 | 2 | ICS27 like implementation adhering to solana-ibc app module. -------------------------------------------------------------------------------- /crates/cvm-glt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Composable Developers"] 3 | edition = "2021" 4 | homepage = "https://composable.finance" 5 | name = "cvm-glt" 6 | version = "0.1.0" -------------------------------------------------------------------------------- /crates/cvm-glt/README.md: -------------------------------------------------------------------------------- 1 | # Global Lookup Table 2 | 3 | Accounts, connections and assets abstractions registry. 4 | 5 | Configuration stored on each routeable chain. 6 | 7 | 8 | Naming inspired by https://solana.com/docs/advanced/lookup-tables . Unlike registry, which usually in native identifiers, it also stores mapping for some global indexes to local native names. -------------------------------------------------------------------------------- /crates/cvm-glt/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/cvm-route/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "cvm-route" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | cosmwasm-std = { workspace = true, features = ["ibc3"] } 8 | cw-storage-plus = { workspace = true, optional = true} 9 | cvm = { workspace = true } 10 | serde = { workspace = true, default-features = false, features = ["derive", "alloc"] } 11 | schemars ={ workspace = true, optional = true} 12 | ibc-core-host-types = { workspace = true, features = [ 13 | "serde", 14 | ] } 15 | ibc-app-transfer-types = { workspace = true, default-features = false, features = ["serde"] } 16 | cosmwasm-schema = {workspace = true, optional = true} 17 | serde-cw-value = { workspace = true, default-features = false } 18 | bs58 = { version = "*", default-features = false, features = ["alloc"] } 19 | hex = {workspace = true, default-features = false} 20 | 21 | [features] 22 | default = ["std", "cosmos",] 23 | cosmwasm = [ 24 | "cvm/cosmwasm", 25 | "dep:cw-storage-plus", 26 | ] 27 | json-schema = [ 28 | "cvm/json-schema", 29 | "ibc-core-host-types/schema", 30 | "dep:schemars", 31 | "dep:cosmwasm-schema", 32 | "ibc-app-transfer-types/schema", 33 | "serde-cw-value/schema", 34 | ] 35 | cosmos = [ 36 | "cvm/cosmos", 37 | ] 38 | std = [ 39 | "cvm/std", 40 | "ibc-core-host-types/std", 41 | "ibc-app-transfer-types/std", 42 | "serde-cw-value/std", 43 | ] 44 | -------------------------------------------------------------------------------- /crates/cvm-route/src/exchange.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use cvm::{exchange::ExchangeId, NetworkId}; 3 | 4 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 5 | #[cfg_attr( 6 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 7 | derive(schemars::JsonSchema) 8 | )] 9 | #[serde(rename_all = "snake_case")] 10 | pub enum ExchangeType { 11 | #[cfg(all(feature = "cosmwasm", feature = "cosmos"))] 12 | OsmosisPoolManagerModuleV1Beta1 { 13 | pool_id: u64, 14 | token_a: String, 15 | token_b: String, 16 | }, 17 | #[cfg(feature = "cosmwasm")] 18 | AstroportRouterContract { 19 | address: cosmwasm_std::Addr, 20 | token_a: String, 21 | token_b: String, 22 | }, 23 | } 24 | 25 | /// allows to execute Exchange instruction 26 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 27 | #[cfg_attr( 28 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 29 | derive(schemars::JsonSchema) 30 | )] 31 | #[serde(rename_all = "snake_case")] 32 | pub struct ExchangeItem { 33 | pub exchange_id: ExchangeId, 34 | pub network_id: NetworkId, 35 | pub exchange: ExchangeType, 36 | // if was set, means that cannot exchange via this venue 37 | pub closed: Option, 38 | } 39 | 40 | impl ExchangeItem { 41 | pub fn new(exchange_id: ExchangeId, network_id: NetworkId, exchange: ExchangeType) -> Self { 42 | Self { 43 | exchange_id, 44 | network_id, 45 | exchange, 46 | closed: None, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/cvm-route/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![cfg_attr(no_std, feature(error_in_core))] 3 | extern crate alloc; 4 | 5 | pub mod asset; 6 | pub mod exchange; 7 | mod prelude; 8 | pub mod primitive_types; 9 | mod solana_program; 10 | pub mod transport; 11 | pub mod venue; 12 | -------------------------------------------------------------------------------- /crates/cvm-route/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use alloc::{string::String, vec, vec::Vec}; 2 | 3 | pub use serde::{Deserialize, Serialize}; 4 | 5 | #[cfg(feature = "parity-scale-codec")] 6 | pub use parity_scale_codec::{Decode, Encode}; 7 | 8 | #[cfg(feature = "parity-scale-codec")] 9 | pub use scale_info::TypeInfo; 10 | -------------------------------------------------------------------------------- /crates/cvm-route/src/primitive_types.rs: -------------------------------------------------------------------------------- 1 | //! paritytech/primitive-types copy until they support std wasm compiled or CW support no_std 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Deserialize, Serialize)] 6 | pub struct H160( 7 | #[serde( 8 | serialize_with = "hex::serialize", 9 | deserialize_with = "hex::deserialize" 10 | )] 11 | #[cfg_attr(feature = "json-schema", schemars(schema_with = "String::json_schema"))] 12 | [u8; 20], 13 | ); 14 | 15 | impl H160 { 16 | pub fn as_bytes(&self) -> &[u8] { 17 | &self.0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /crates/cvm-route/src/solana_program.rs: -------------------------------------------------------------------------------- 1 | //! solana_program types until they support serde/json-schema 2 | 3 | use core::fmt::Display; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /// Is `solana-program` crate `Pubkey` type, but with proper serde support into base58 encoding. 8 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 9 | #[serde(rename_all = "snake_case")] 10 | #[cfg_attr( 11 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 12 | derive(schemars::JsonSchema) 13 | )] 14 | pub struct Pubkey(pub(crate) [u8; 32]); 15 | -------------------------------------------------------------------------------- /crates/cvm-route/src/venue.rs: -------------------------------------------------------------------------------- 1 | //! expresses asset transformation regardless of way transforamtion is done 2 | 3 | use crate::prelude::*; 4 | use cvm::{exchange::ExchangeId, AssetId}; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 7 | #[cfg_attr( 8 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 9 | derive(schemars::JsonSchema) 10 | )] 11 | #[serde(rename_all = "snake_case")] 12 | pub enum VenueId { 13 | Exchange(ExchangeId), 14 | Transfer, 15 | } 16 | 17 | /// assets which can be transomed into each other via venue 18 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 19 | #[cfg_attr( 20 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 21 | derive(schemars::JsonSchema) 22 | )] 23 | #[serde(rename_all = "snake_case")] 24 | pub struct AssetsVenueItem { 25 | pub venue_id: VenueId, 26 | pub from_asset_id: AssetId, 27 | pub to_asset_id: AssetId, 28 | } 29 | 30 | impl AssetsVenueItem { 31 | pub fn new(venue_id: VenueId, from_asset_id: AssetId, to_asset_id: AssetId) -> Self { 32 | Self { 33 | venue_id, 34 | from_asset_id, 35 | to_asset_id, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "cvm-runtime-exchange" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | cosmwasm-std = { workspace = true, features = ["ibc3"] } 8 | cvm = { workspace = true, default-features = false } 9 | cvm-route = { workspace = true, default-features = false } 10 | cvm-runtime = { workspace = true, default-features = false } 11 | astroport = { workspace = true, default-features = false } 12 | prost = { workspace = true, default-features = false} 13 | serde = { workspace = true, default-features = false} 14 | ibc-apps-more = { workspace = true, default-features = false} 15 | schemars ={ workspace = true, default-features = false, optional = true} 16 | thiserror = { workspace = true, default-features = false} 17 | [features] 18 | default = [ 19 | "std", "cosmos", 20 | ] 21 | cosmwasm = [ 22 | "cvm/cosmwasm", 23 | "cvm-route/cosmwasm", 24 | "cvm-runtime/cosmwasm", 25 | ] 26 | json-schema = [ 27 | "cvm/json-schema", 28 | "cvm-route/json-schema", 29 | "cvm-runtime/json-schema", 30 | "ibc-apps-more/json-schema", 31 | "dep:schemars", 32 | ] 33 | cosmos = [ 34 | "cvm-route/cosmos", 35 | "cvm-runtime/cosmos", 36 | ] 37 | std = [ 38 | "cvm/std", 39 | "cvm-route/std", 40 | "cvm-runtime/std", 41 | "ibc-apps-more/std", 42 | ] 43 | 44 | 45 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum ContractError { 6 | #[error("Only single asset exchange is supported by pool")] 7 | OnlySingleAssetExchangeIsSupportedByPool, 8 | 9 | /// for the case when specific pool does not supports slippage 10 | #[error("Exchange does not support slippage")] 11 | ExchangeDoesNotSupportSlippage, 12 | 13 | #[error("Cannot define both slippage and limit at same time")] 14 | CannotDefineBothSlippageAndLimitAtSameTime, 15 | 16 | #[error("Asset not found: {0}")] 17 | AssetNotFound(StdError), 18 | #[error("Exchange not found: {0}")] 19 | ExchangeNotFound(StdError), 20 | 21 | #[error("An error occured while doing arithmetic operations")] 22 | ArithmeticError, 23 | 24 | #[error("Program is invalid")] 25 | InvalidProgram, 26 | 27 | #[error("{0}")] 28 | Std(#[from] StdError), 29 | } 30 | 31 | impl From for ContractError { 32 | fn from(_: cvm::ArithmeticError) -> Self { 33 | ContractError::InvalidProgram 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/osmosis_std/mod.rs: -------------------------------------------------------------------------------- 1 | // basically copy https://github.com/osmosis-labs/osmosis-rust/tree/main/packages/osmosis-std 2 | // until they handle all deps to be no_std 3 | pub mod types; 4 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/osmosis_std/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod osmosis; 2 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/osmosis_std/types/osmosis/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod poolmanager; 2 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/osmosis_std/types/osmosis/poolmanager/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod v1beta1; 2 | -------------------------------------------------------------------------------- /crates/cvm-runtime-exchange/src/osmosis_std/types/osmosis/poolmanager/v1beta1.rs: -------------------------------------------------------------------------------- 1 | use cvm_runtime::prelude::*; 2 | 3 | #[allow(clippy::derive_partial_eq_without_eq)] 4 | #[derive(Clone, PartialEq, Eq, prost::Message, Serialize, Deserialize)] 5 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 6 | pub struct MsgSwapExactAmountIn { 7 | #[prost(string, tag = "1")] 8 | pub sender: String, 9 | #[prost(message, repeated, tag = "2")] 10 | pub routes: Vec, 11 | #[prost(message, optional, tag = "3")] 12 | pub token_in: ::core::option::Option, 13 | #[prost(string, tag = "4")] 14 | pub token_out_min_amount: String, 15 | } 16 | 17 | impl MsgSwapExactAmountIn { 18 | pub const TYPE_URL: &'static str = "/osmosis.poolmanager.v1beta1.MsgSwapExactAmountIn"; 19 | } 20 | 21 | #[allow(clippy::derive_partial_eq_without_eq)] 22 | #[derive(Clone, PartialEq, Eq, prost::Message, Serialize, Deserialize)] 23 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 24 | pub struct SwapAmountInRoute { 25 | #[prost(uint64, tag = "1")] 26 | #[serde(alias = "poolID")] 27 | pub pool_id: u64, 28 | #[prost(string, tag = "2")] 29 | pub token_out_denom: String, 30 | } 31 | -------------------------------------------------------------------------------- /crates/cvm-runtime/.devenv/profile: -------------------------------------------------------------------------------- 1 | /nix/store/qhq84xind06z73abs3486szc61cj3d71-devenv-profile -------------------------------------------------------------------------------- /crates/cvm-runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Composable Developers"] 3 | edition = "2021" 4 | homepage = "https://composable.finance" 5 | name = "cvm-runtime" 6 | description = "CVM runner on various chains" 7 | version = "0.1.0" 8 | 9 | [dependencies] 10 | bech32 = { workspace = true, features = [] } 11 | cosmwasm-schema = { workspace = true, optional = true } 12 | cosmwasm-std = { workspace = true, features = ["ibc3"] } 13 | cw-storage-plus = { workspace = true, optional = true } 14 | cw20 = { workspace = true, optional = true } 15 | derive_more.workspace = true 16 | hex = { workspace = true, features = ["alloc", "serde"] } 17 | ibc-app-transfer-types = { workspace = true, features = [ 18 | "serde", 19 | "parity-scale-codec", 20 | ] } 21 | ibc-core-host-types = { workspace = true, features = ["serde"] } 22 | num-rational = { workspace = true, default-features = false} 23 | parity-scale-codec = { workspace = true, optional = true } 24 | prost = { workspace = true, features = ["prost-derive"] } 25 | scale-info = { workspace = true, features = ["derive"], optional = true } 26 | schemars = { workspace = true, optional = true } 27 | serde = { workspace = true, features = ["alloc", "derive"] } 28 | serde-json-wasm = { workspace = true, default-features = false } 29 | sha2 = { workspace = true } 30 | strum.workspace = { workspace = true } 31 | thiserror = { workspace = true } 32 | ibc-proto = { workspace = true, default-features = false, features = ["serde"] } 33 | serde-cw-value = { workspace = true, default-features = false } 34 | ibc-primitives = { workspace = true, default-features = false } 35 | # xcm = { workspace = true, default-features = false, optional = true } 36 | # primitive-types = { workspace = true, features = [ 37 | # "serde_no_std", 38 | # ], default-features = false } 39 | 40 | cvm = { workspace = true, default-features = false } 41 | cvm-route = { workspace = true, default-features = false } 42 | ibc-apps-more = { workspace = true, default-features = false } 43 | 44 | [build-dependencies] 45 | prost-build = { workspace = true } 46 | 47 | [features] 48 | default = ["std", "cosmos"] 49 | # xcm = ["dep:xcm"] 50 | paroty-scale-codec = [ 51 | "parity-scale-codec", 52 | "ibc-proto/parity-scale-codec", 53 | "cvm/parity-scale-codec", 54 | "scale-info", 55 | "ibc-app-transfer-types/parity-scale-codec", 56 | "ibc-core-host-types/parity-scale-codec", 57 | "ibc-primitives/parity-scale-codec", 58 | ] 59 | cosmos = [ 60 | "cvm-route/cosmos", 61 | "cvm/cosmos", 62 | ] 63 | cosmwasm = ["cw-storage-plus", "cw20", "ibc-apps-more/cosmwasm", "cvm/cosmwasm", "cvm-route/cosmwasm",] 64 | # substrate = ["xcm"] 65 | json-schema = [ 66 | "serde-cw-value/schema", 67 | "ibc-apps-more/json-schema", 68 | "cvm/json-schema", 69 | "cvm-route/json-schema", 70 | "dep:cosmwasm-schema", 71 | "dep:schemars", 72 | "ibc-app-transfer-types/schema", 73 | "ibc-primitives/schema", 74 | "ibc-core-host-types/schema", 75 | #"primitive-types/json-schema", 76 | "serde-cw-value/schema", 77 | ] 78 | 79 | std = [ 80 | "cvm/std", 81 | "cvm-route/std", 82 | "ibc-proto/std", 83 | "serde-cw-value/std", 84 | "serde-json-wasm/std", 85 | "ibc-app-transfer-types/schema", 86 | "ibc-core-host-types/schema", 87 | "ibc-primitives/schema", 88 | "thiserror/std", 89 | ] 90 | -------------------------------------------------------------------------------- /crates/cvm-runtime/build.rs: -------------------------------------------------------------------------------- 1 | extern crate prost_build; 2 | 3 | const PROTOS_DIR: &str = "proto"; 4 | 5 | fn main() -> std::io::Result<()> { 6 | let mut files = Vec::new(); 7 | let path = std::path::Path::new(PROTOS_DIR); 8 | let dir = std::fs::read_dir(path)?; 9 | 10 | add_files(dir, &mut files)?; 11 | 12 | if files.is_empty() { 13 | println!("No proto files found in {:?}.", path); 14 | } 15 | prost_build::compile_protos(&files, &[PROTOS_DIR]) 16 | } 17 | 18 | fn add_files(dir: std::fs::ReadDir, files: &mut Vec) -> Result<(), std::io::Error> { 19 | for entry in dir { 20 | if let Ok(name) = entry?.file_name().into_string() { 21 | if !name.starts_with('.') && name.ends_with(".proto") { 22 | files.push([PROTOS_DIR, "/", name.as_str()].concat()) 23 | } 24 | } 25 | } 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /crates/cvm-runtime/proto/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package cvm.common; 4 | 5 | message Uint128 { 6 | uint64 highBits = 1; 7 | uint64 lowBits = 2; 8 | 9 | // next tag: 3 10 | } 11 | -------------------------------------------------------------------------------- /crates/cvm-runtime/proto/program.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "common.proto"; 4 | 5 | package cvm.program; 6 | 7 | message PacketAsset { 8 | cvm.common.Uint128 asset_id = 1; 9 | cvm.common.Uint128 amount = 2; 10 | 11 | // next tag: 3 12 | } 13 | 14 | message Packet { 15 | bytes executor = 1; 16 | UserOrigin user_origin = 2; 17 | bytes salt = 3; 18 | Program program = 4; 19 | repeated PacketAsset assets = 5; 20 | 21 | // next tag: 6 22 | } 23 | 24 | message UserOrigin { 25 | uint32 network_id = 1; 26 | bytes account = 2; 27 | 28 | // next tag: 3 29 | } 30 | 31 | message Program { 32 | bytes tag = 1; 33 | repeated Instruction instructions = 2; 34 | 35 | // next tag: 3 36 | } 37 | 38 | message Instruction { 39 | oneof instruction { 40 | Transfer transfer = 1; 41 | Spawn spawn = 2; 42 | Call call = 3; 43 | Exchange exchange = 4; 44 | } 45 | 46 | // next tag: 5 47 | } 48 | 49 | message Ratio { 50 | /// lest or equal to denominator 51 | uint64 nominator = 1; 52 | // `denominator = 1_000_000_000_000_000_000` always 53 | } 54 | 55 | message Absolute { 56 | cvm.common.Uint128 value = 1; 57 | // next tag: 2 58 | } 59 | 60 | // in case of no fileds is just zero amount 61 | // if only ratio, when app,lied to absolute amount in register or virutal wallet, is part of it 62 | // absolute is just absolute amount taken from register or virtual wallet 63 | // in case of both, absolute is taken, and from remaining, part is added 64 | message Balance { 65 | optional Absolute absolute = 1; 66 | optional Ratio ratio = 2; 67 | 68 | // next tag: 3 69 | } 70 | 71 | message Asset { 72 | cvm.common.Uint128 asset_id = 1; 73 | Balance balance = 2; 74 | 75 | // next tag: 3 76 | } 77 | 78 | message AssetAmount { 79 | cvm.common.Uint128 asset_id = 1; 80 | Balance balance = 2; 81 | 82 | // next tag: 3 83 | } 84 | 85 | message BindingValue { 86 | oneof type { 87 | Register register = 1; 88 | cvm.common.Uint128 asset_id = 2; 89 | AssetAmount asset_amount = 3; 90 | } 91 | 92 | // next tag: 4 93 | } 94 | 95 | enum Register { 96 | IP = 0; 97 | TIP = 1; 98 | THIS = 2; 99 | RESULT = 3; 100 | CARRY = 4; 101 | } 102 | 103 | message Binding { 104 | uint32 position = 1; 105 | BindingValue binding_value = 2; 106 | 107 | // next tag: 3 108 | } 109 | 110 | message Transfer { 111 | oneof account_type{ 112 | bytes account = 1; 113 | Tip tip = 2; 114 | } 115 | repeated Asset assets = 3; 116 | 117 | // next tag: 4 118 | } 119 | 120 | message Tip { 121 | // next tag: 1 122 | } 123 | 124 | message Exchange { 125 | cvm.common.Uint128 exchange_id = 1; 126 | repeated Asset give = 5; 127 | repeated Asset want = 6; 128 | 129 | // next tag: 7 130 | } 131 | 132 | message Spawn { 133 | uint32 network_id = 1; 134 | bytes salt = 3; 135 | Program program = 4; 136 | repeated Asset assets = 5; 137 | 138 | // next tag: 6 139 | } 140 | 141 | message Call { 142 | bytes payload = 1; 143 | repeated Binding bindings = 2; 144 | 145 | // next tag: 3 146 | } 147 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/bin/outpost.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(feature = "json-schema", not(target_arch = "wasm32")))] 2 | #[allow(clippy::disallowed_methods)] 3 | fn main() { 4 | use cosmwasm_schema::write_api; 5 | use cvm_runtime::outpost; 6 | 7 | write_api! { 8 | instantiate: outpost::InstantiateMsg, 9 | query: outpost::QueryMsg, 10 | execute: outpost::ExecuteMsg, 11 | } 12 | } 13 | 14 | #[cfg(not(all(feature = "json-schema", not(target_arch = "wasm32"))))] 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/bridge.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | use crate::{NetworkId, UserOrigin}; 4 | 5 | /// The Origin that executed the CVM operation. 6 | /// Origin was verified to satisfy security semantics for execution. 7 | #[cfg_attr( 8 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 9 | derive(schemars::JsonSchema) 10 | )] 11 | #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] 12 | pub enum CallOrigin { 13 | Remote { user_origin: UserOrigin }, 14 | Local { user: Addr }, 15 | } 16 | 17 | impl CallOrigin { 18 | /// Extract the user from a [`CallOrigin`]. 19 | /// No distinction is done for local or remote user in this case. 20 | pub fn user(&self, current_network: NetworkId) -> UserOrigin { 21 | match self { 22 | CallOrigin::Remote { user_origin } => user_origin.clone(), 23 | CallOrigin::Local { user } => UserOrigin { 24 | network_id: current_network, 25 | user_id: user.as_bytes().to_vec().into(), 26 | }, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/executor.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::new_ret_no_self)] 2 | use cosmwasm_std::Event; 3 | 4 | use crate::prelude::*; 5 | use crate::shared::CvmProgram; 6 | use crate::ExecutorOrigin; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 9 | #[cfg_attr( 10 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 11 | derive(schemars::JsonSchema) 12 | )] 13 | #[serde(rename_all = "snake_case")] 14 | pub struct InstantiateMsg { 15 | /// Address of the CVM outpost. 16 | pub outpost_address: String, 17 | /// The executor origin. 18 | pub executor_origin: ExecutorOrigin, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 22 | #[cfg_attr( 23 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 24 | derive(schemars::JsonSchema) 25 | )] 26 | #[serde(rename_all = "snake_case")] 27 | pub struct Step { 28 | /// Tip party facilitated bridging and execution. 29 | pub tip: Addr, 30 | /// The current instruction pointer in the program. 31 | /// Note that the [`Step::program`] instructions are poped when executed, we can't rely on this 32 | /// instruction pointer to index into the instructions. In fact, this pointer tells us how many 33 | /// instructions we already consumed. 34 | pub instruction_pointer: u16, 35 | /// The next instructions to execute (actual program). 36 | pub program: CvmProgram, 37 | } 38 | 39 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 40 | #[cfg_attr( 41 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 42 | derive(schemars::JsonSchema) 43 | )] 44 | #[serde(rename_all = "snake_case")] 45 | pub enum ExecuteMsg { 46 | /// Execute an CVM program 47 | Execute { tip: Addr, program: CvmProgram }, 48 | 49 | /// This is only meant to be used by the executor itself, otherwise it will return an error 50 | /// The existence of this message is to allow the execution of the `Call` instruction. Once we 51 | /// hit a call, the program queue the call and queue itself after it to ensure that the side 52 | /// effect of the call has been executed. 53 | ExecuteStep { step: Step }, 54 | /// Add owners of this contract 55 | AddOwners { owners: Vec }, 56 | /// Remove owners from the contract 57 | RemoveOwners { owners: Vec }, 58 | /// spawn is cross chain, so sometimes errors are came from other blocks 59 | /// so gateway can set that error on executor 60 | SetErr { reason: String }, 61 | } 62 | 63 | impl CvmExecutorInstantiated { 64 | pub const NAME: &'static str = "cvm.executor.instantiated"; 65 | pub const EXECUTOR_ORIGIN: &'static str = "executor_origin"; 66 | #[cfg(feature = "cosmwasm")] 67 | pub fn new(executor_origin: &ExecutorOrigin) -> Event { 68 | use crate::shared::to_json_base64; 69 | 70 | Event::new(Self::NAME).add_attribute( 71 | Self::EXECUTOR_ORIGIN, 72 | to_json_base64(executor_origin).expect("origin is managed by"), 73 | ) 74 | } 75 | } 76 | 77 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 78 | #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] 79 | #[serde(rename = "cvm.executor.instantiated")] 80 | pub struct CvmExecutorInstantiated { 81 | pub executor_origin: String, 82 | } 83 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/packet.rs: -------------------------------------------------------------------------------- 1 | use crate::{Funds, UserOrigin}; 2 | use alloc::{string::String, vec::Vec}; 3 | #[cfg(feature = "cosmwasm")] 4 | use cosmwasm_std::Binary; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 8 | pub enum XCVMAck { 9 | Ok, 10 | Fail, 11 | } 12 | 13 | impl XCVMAck { 14 | fn into_byte(self) -> u8 { 15 | match self { 16 | Self::Ok => 0, 17 | Self::Fail => 1, 18 | } 19 | } 20 | 21 | fn try_from_byte(value: u8) -> Result { 22 | match value { 23 | 0 => Ok(Self::Ok), 24 | 1 => Ok(Self::Fail), 25 | _ => Err(()), 26 | } 27 | } 28 | } 29 | 30 | #[cfg(feature = "cosmwasm")] 31 | impl From for Binary { 32 | fn from(value: XCVMAck) -> Self { 33 | Binary::from(Vec::from(value)) 34 | } 35 | } 36 | 37 | impl From for Vec { 38 | fn from(value: XCVMAck) -> Self { 39 | [value.into_byte()].to_vec() 40 | } 41 | } 42 | 43 | impl From for String { 44 | fn from(ack: XCVMAck) -> Self { 45 | let digit = [b'0' + ack.into_byte()]; 46 | // SAFETY: digit is always an ASCII digit 47 | Self::from(unsafe { core::str::from_utf8_unchecked(&digit[..]) }) 48 | } 49 | } 50 | 51 | impl TryFrom<&[u8]> for XCVMAck { 52 | type Error = (); 53 | fn try_from(value: &[u8]) -> Result { 54 | match value { 55 | [byte] => Self::try_from_byte(*byte), 56 | _ => Err(()), 57 | } 58 | } 59 | } 60 | 61 | #[cfg_attr( 62 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 63 | derive(schemars::JsonSchema) 64 | )] 65 | #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] 66 | #[serde(rename_all = "snake_case")] 67 | pub struct Packet { 68 | /// The executor that was the origin of this packet. 69 | #[serde(with = "hex")] 70 | #[cfg_attr( 71 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 72 | schemars(with = "String") 73 | )] 74 | pub executor: Vec, 75 | /// The user that originated the first CVM call. 76 | pub user_origin: UserOrigin, 77 | /// The salt associated with the program. 78 | #[serde(with = "hex")] 79 | #[cfg_attr( 80 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 81 | schemars(with = "String") 82 | )] 83 | pub salt: Vec, 84 | /// The protobuf encoded program. 85 | pub program: Program, 86 | /// The assets that were attached to the program. 87 | pub assets: Funds>, 88 | } 89 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use alloc::{ 2 | boxed::Box, 3 | format, 4 | string::{String, ToString}, 5 | vec, 6 | vec::Vec, 7 | }; 8 | 9 | pub use core::{fmt::Display, str::FromStr}; 10 | pub use cosmwasm_std::{Addr, Binary, Coin, Uint128}; 11 | pub use serde::{Deserialize, Serialize}; 12 | 13 | #[cfg(feature = "parity-scale-codec")] 14 | pub use parity_scale_codec::{Decode, Encode}; 15 | 16 | #[cfg(feature = "parity-scale-codec")] 17 | pub use scale_info::TypeInfo; 18 | 19 | #[cfg(all(feature = "json-schema", not(target_arch = "wasm32")))] 20 | pub use cosmwasm_schema::QueryResponses; 21 | 22 | #[cfg(all(feature = "json-schema", not(target_arch = "wasm32")))] 23 | pub use schemars::JsonSchema; 24 | 25 | pub use ibc_app_transfer_types::PrefixedDenom; 26 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/program.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | #[cfg_attr( 4 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 5 | derive(schemars::JsonSchema) 6 | )] 7 | #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] 8 | #[serde(rename_all = "snake_case")] 9 | pub struct Program { 10 | /// In JSON, hex encoded identifiers to identify the program off chain (for example in 11 | /// indexer). 12 | #[serde( 13 | serialize_with = "hex::serialize", 14 | deserialize_with = "hex::deserialize" 15 | )] 16 | #[cfg_attr( 17 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 18 | schemars(schema_with = "String::json_schema") 19 | )] 20 | #[serde(skip_serializing_if = "Vec::is_empty", default)] 21 | pub tag: Vec, 22 | /// list of instructions to be executed 23 | pub instructions: Instructions, 24 | } 25 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/proto/pb.rs: -------------------------------------------------------------------------------- 1 | pub mod solidity { 2 | include!(concat!(env!("OUT_DIR"), "/solidity.rs")); 3 | } 4 | pub mod program { 5 | include!(concat!(env!("OUT_DIR"), "/cvm.program.rs")); 6 | } 7 | 8 | pub mod common { 9 | pub use cvm::proto::*; 10 | } 11 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/proto/result.rs: -------------------------------------------------------------------------------- 1 | use super::{Isomorphism, NonEmptyExt}; 2 | use prost::Message; 3 | 4 | /// A generic definition of a protocol message with an Ok and Err variants. 5 | #[derive(Clone, PartialEq, Message)] 6 | pub struct ResultMessage { 7 | #[prost(oneof = "ResultEnum", tags = "1, 2")] 8 | pub result: core::option::Option>, 9 | } 10 | 11 | /// Nested enum type in [`ResultMessage`]. 12 | #[derive(Clone, PartialEq, prost::Oneof)] 13 | pub enum ResultEnum { 14 | #[prost(message, tag = "1")] 15 | Ok(T), 16 | #[prost(message, tag = "2")] 17 | Err(E), 18 | } 19 | 20 | impl Isomorphism for Result 21 | where 22 | T: Isomorphism, 23 | T::Message: Default + Send + Sync + TryInto + From, 24 | E: Isomorphism, 25 | E::Message: Default + Send + Sync + TryInto + From, 26 | { 27 | type Message = ResultMessage; 28 | } 29 | 30 | impl TryFrom> 31 | for Result 32 | { 33 | type Error = (); 34 | fn try_from(result: ResultMessage) -> Result { 35 | match result.result.non_empty()? { 36 | ResultEnum::Ok(ok) => Ok(Ok(ok.try_into().map_err(|_| ())?)), 37 | ResultEnum::Err(ok) => Ok(Err(ok.try_into().map_err(|_| ())?)), 38 | } 39 | } 40 | } 41 | 42 | impl From> for ResultMessage { 43 | fn from(result: Result) -> Self { 44 | let result = match result { 45 | Ok(ok) => ResultEnum::Ok(ok.into()), 46 | Err(err) => ResultEnum::Err(err.into()), 47 | }; 48 | Self { 49 | result: Some(result), 50 | } 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_encoding() { 56 | let want = Result::::Ok("ok".into()); 57 | let encoded = want.clone().encode(); 58 | let got = Result::::decode(encoded.as_slice()).unwrap(); 59 | assert_eq!(want, got); 60 | 61 | let want = Result::::Err("err".into()); 62 | let encoded = want.clone().encode(); 63 | let got = Result::::decode(encoded.as_slice()).unwrap(); 64 | assert_eq!(want, got); 65 | } 66 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use crate::network::Network; 2 | 3 | pub trait Protocol { 4 | type Error; 5 | fn serialize(&self) -> Result; 6 | } 7 | -------------------------------------------------------------------------------- /crates/cvm-runtime/src/transport/mod.rs: -------------------------------------------------------------------------------- 1 | //! converts CVM programs to relevant carriers representation 2 | 3 | pub mod ibc; 4 | 5 | #[cfg(feature = "xcm")] 6 | pub mod xcm; 7 | -------------------------------------------------------------------------------- /crates/cvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "cvm" 4 | version = "0.1.0" 5 | description = "Core data types to be shared with any environment on chain and offchain" 6 | 7 | [dependencies] 8 | 9 | cosmwasm-schema = { workspace = true, optional = true } 10 | cosmwasm-std = { workspace = true, no-default-features = true, optional = true} 11 | cw-storage-plus = { workspace = true, optional = true} 12 | 13 | parity-scale-codec = { workspace = true, optional = true } 14 | prost = { workspace = true, features = ["prost-derive"] } 15 | scale-info = { workspace = true, features = ["derive"], optional = true } 16 | schemars = { workspace = true, optional = true } 17 | serde = { workspace = true, features = ["alloc", "derive"] } 18 | hex ={ workspace = true, default-features = false, features = ["serde"]} 19 | bech32 = { workspace = true, default-features = false} 20 | derive_more = { workspace = true, default-features = false} 21 | num-traits = { workspace = true, default-features = false} 22 | 23 | [dev-dependencies] 24 | serde-json-wasm = { workspace = true, default-features = false } 25 | 26 | [build-dependencies] 27 | prost-build = { workspace = true } 28 | 29 | [features] 30 | default = ["std"] 31 | cw-storage-plus = [] 32 | parity-scale-codec = [ 33 | "dep:parity-scale-codec", 34 | "dep:scale-info", 35 | ] 36 | cosmos = [] 37 | cosmwasm = ["dep:cosmwasm-std", "dep:cw-storage-plus",] 38 | json-schema = [ 39 | "dep:cosmwasm-schema", 40 | "dep:schemars", 41 | ] 42 | 43 | std = [ 44 | ] -------------------------------------------------------------------------------- /crates/cvm/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/crates/cvm/README.md -------------------------------------------------------------------------------- /crates/cvm/build.rs: -------------------------------------------------------------------------------- 1 | extern crate prost_build; 2 | 3 | const PROTOS_DIR: &str = "proto"; 4 | 5 | fn main() -> std::io::Result<()> { 6 | let mut files = Vec::new(); 7 | let path = std::path::Path::new(PROTOS_DIR); 8 | let dir = std::fs::read_dir(path)?; 9 | for entry in dir { 10 | if let Ok(name) = entry?.file_name().into_string() { 11 | if !name.starts_with('.') && name.ends_with(".proto") { 12 | files.push([PROTOS_DIR, "/", name.as_str()].concat()) 13 | } 14 | } 15 | } 16 | if files.is_empty() { 17 | println!("No proto files found in {:?}.", path); 18 | } 19 | prost_build::compile_protos(&files, &[PROTOS_DIR]) 20 | } 21 | -------------------------------------------------------------------------------- /crates/cvm/proto/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package cvm.common; 4 | 5 | message Uint128 { 6 | uint64 highBits = 1; 7 | uint64 lowBits = 2; 8 | 9 | // next tag: 3 10 | } 11 | -------------------------------------------------------------------------------- /crates/cvm/src/exchange.rs: -------------------------------------------------------------------------------- 1 | pub type ExchangeId = crate::shared::Displayed; 2 | -------------------------------------------------------------------------------- /crates/cvm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![cfg_attr(no_std, feature(error_in_core))] 3 | extern crate alloc; 4 | 5 | pub mod address; 6 | pub mod asset; 7 | pub mod exchange; 8 | pub mod network; 9 | mod prelude; 10 | pub mod proto; 11 | pub mod shared; 12 | pub use address::*; 13 | pub use asset::*; 14 | pub use network::*; 15 | -------------------------------------------------------------------------------- /crates/cvm/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use alloc::{ 2 | string::{String, ToString}, 3 | vec, 4 | vec::Vec, 5 | }; 6 | 7 | pub use core::{fmt::Display, str::FromStr}; 8 | 9 | pub use serde::{Deserialize, Serialize}; 10 | 11 | #[cfg(feature = "parity-scale-codec")] 12 | pub use parity_scale_codec::{Decode, Encode}; 13 | 14 | #[cfg(feature = "parity-scale-codec")] 15 | pub use scale_info::TypeInfo; 16 | 17 | #[cfg(all(feature = "json-schema", not(target_arch = "wasm32")))] 18 | pub use schemars::JsonSchema; 19 | -------------------------------------------------------------------------------- /crates/cvm/src/proto.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/cvm.common.rs")); 2 | 3 | impl From for u128 { 4 | fn from(value: super::proto::Uint128) -> Self { 5 | ((value.high_bits as u128) << 64) | value.low_bits as u128 6 | } 7 | } 8 | 9 | impl From for super::proto::Uint128 { 10 | fn from(value: u128) -> Self { 11 | Self { 12 | high_bits: (value >> 64) as u64, 13 | low_bits: value as u64, 14 | } 15 | } 16 | } 17 | 18 | impl From for crate::AssetId { 19 | fn from(value: super::proto::Uint128) -> Self { 20 | u128::from(value).into() 21 | } 22 | } 23 | 24 | impl From for super::proto::Uint128 { 25 | fn from(value: crate::AssetId) -> Self { 26 | u128::from(value).into() 27 | } 28 | } 29 | 30 | #[test] 31 | fn test_u128_uint128_conversion() { 32 | let value = 0xDEAD_0000_0000_0000_BEEF_0000_0000_0000_u128; 33 | let (high_bits, low_bits) = (0xDEAD_0000_0000_0000, 0xBEEF_0000_0000_0000); 34 | let msg = super::proto::Uint128 { 35 | high_bits, 36 | low_bits, 37 | }; 38 | 39 | assert_eq!(msg, super::proto::Uint128::from(value)); 40 | assert_eq!(value, u128::from(msg)); 41 | } 42 | -------------------------------------------------------------------------------- /crates/mantis-cw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mantis-cw" 3 | version = {workspace = true} 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | 9 | [dependencies] 10 | cosmwasm-std = { workspace = true, features = [ 11 | "iterator", 12 | ], default-features = false } 13 | 14 | 15 | serde = {workspace = true} 16 | 17 | cosmwasm-schema = { workspace = true, default-features = false } 18 | cw-storage-plus = { workspace = true, features = [ 19 | "iterator", 20 | ], default-features = false } 21 | 22 | tuples = { workspace = true } 23 | 24 | derive_more = { workspace = true, features = [] } 25 | 26 | strum_macros = { workspace = true } 27 | 28 | 29 | rust_decimal = { workspace = true } -------------------------------------------------------------------------------- /crates/mantis-cw/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Some reusable CW code for Mantis. 4 | 5 | Integrates cosmrs, ibc-rs, cw core types. -------------------------------------------------------------------------------- /crates/mantis-cw/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ordered_coin_pair; 2 | mod ordered_tuple; 3 | 4 | pub use ordered_coin_pair::OrderCoinPair; 5 | pub use ordered_tuple::OrderedTuple2; 6 | use rust_decimal::Decimal; 7 | 8 | pub type Denom = String; 9 | pub type DenomPair = OrderedTuple2; 10 | 11 | /// this is buy sell in terms of token1/token2 or A/B. just 2 sides of the orderbook. 12 | /// not Buy and Sell orders which differ in limit definition(in limit vs out limit). 13 | #[derive(Debug, PartialEq, Eq, Clone, Copy, strum_macros::AsRefStr, derive_more::Display)] 14 | #[cfg_attr( 15 | feature = "json-schema", // all(feature = "json-schema", not(target_arch = "wasm32")), 16 | derive(schemars::JsonSchema) 17 | )] 18 | #[derive(serde::Serialize, serde::Deserialize)] 19 | #[serde(rename_all = "snake_case")] 20 | 21 | pub enum OrderSide { 22 | A = 0, 23 | B = 1, 24 | } 25 | 26 | impl OrderSide { 27 | // ddo not use decimal, decimal is bad 28 | pub fn is_acceptable_price(&self, price: Decimal, limit_price: Decimal) -> bool { 29 | match self { 30 | OrderSide::B => price >= limit_price, 31 | OrderSide::A => price <= limit_price, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/mantis-cw/src/ordered_coin_pair.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Coin, Uint128}; 2 | 3 | use crate::{Denom, OrderedTuple2}; 4 | 5 | /// CosmWasm Coin pair ordered by denom 6 | pub struct OrderCoinPair { 7 | pub a: cosmwasm_std::Coin, 8 | pub b: cosmwasm_std::Coin, 9 | } 10 | 11 | impl OrderCoinPair { 12 | pub fn zero(a: Denom, b: Denom) -> Self { 13 | let ab = OrderedTuple2::new(a, b); 14 | Self { 15 | a: cosmwasm_std::Coin { 16 | denom: ab.a, 17 | ..Default::default() 18 | }, 19 | b: cosmwasm_std::Coin { 20 | denom: ab.b, 21 | ..Default::default() 22 | }, 23 | } 24 | } 25 | 26 | pub fn add_a(&mut self, amount: Uint128) { 27 | self.a.amount += amount; 28 | } 29 | 30 | pub fn add_b(&mut self, amount: Uint128) { 31 | self.b.amount += amount; 32 | } 33 | 34 | pub fn add(&mut self, coin: &Coin) { 35 | if coin.denom == self.a.denom { 36 | self.a.amount += coin.amount; 37 | } else if coin.denom == self.b.denom { 38 | self.b.amount += coin.amount; 39 | } else { 40 | panic!("invalid coin denom"); 41 | } 42 | } 43 | } 44 | 45 | impl From<(Denom, Denom)> for OrderCoinPair { 46 | fn from(ab: (Denom, Denom)) -> Self { 47 | Self::zero(ab.0, ab.1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/mantis-cw/src/ordered_tuple.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::{StdError, StdResult}; 3 | use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; 4 | pub use tuples::*; 5 | 6 | /// Ordered tuple can be considered as edge of undirected graph 7 | #[cw_serde] 8 | #[derive(Eq)] 9 | pub struct OrderedTuple2 { 10 | // rust does not allows 'r#1' 11 | pub a: T, 12 | pub b: T, 13 | } 14 | 15 | impl<'a, T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a>> PrimaryKey<'a> 16 | for OrderedTuple2 17 | { 18 | type Prefix = T; 19 | 20 | type SubPrefix = T; 21 | 22 | type Suffix = (); 23 | 24 | type SuperSuffix = (T, T); 25 | fn key(&self) -> Vec { 26 | let mut keys = self.a.key(); 27 | keys.extend(self.b.key()); 28 | keys 29 | } 30 | } 31 | 32 | impl<'a, T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a>> Prefixer<'a> 33 | for OrderedTuple2 34 | { 35 | fn prefix(&self) -> Vec { 36 | let mut res = self.a.prefix(); 37 | res.extend(self.b.prefix()); 38 | res 39 | } 40 | } 41 | 42 | impl< 43 | 'a, 44 | T: Clone + Prefixer<'a> + KeyDeserialize + PrimaryKey<'a> + PartialEq + PartialOrd, 45 | > KeyDeserialize for OrderedTuple2 46 | { 47 | type Output = OrderedTuple2; 48 | 49 | #[inline(always)] 50 | fn from_vec(mut value: Vec) -> StdResult { 51 | let mut tu = value.split_off(2); 52 | let t_len = parse_length(&value)?; 53 | let u = tu.split_off(t_len); 54 | 55 | Ok(Self::new(T::from_vec(tu)?, T::from_vec(u)?)) 56 | } 57 | } 58 | 59 | fn parse_length(value: &[u8]) -> StdResult { 60 | Ok(u16::from_be_bytes( 61 | value 62 | .try_into() 63 | .map_err(|_| StdError::generic_err("Could not read 2 byte length"))?, 64 | ) 65 | .into()) 66 | } 67 | 68 | impl Tuple for OrderedTuple2 { 69 | fn arity(&self) -> usize { 70 | 2 71 | } 72 | } 73 | 74 | impl Tuple2 for OrderedTuple2 { 75 | type Item0 = T; 76 | type Item1 = T; 77 | } 78 | 79 | impl From> for (T, T) { 80 | fn from(val: OrderedTuple2) -> Self { 81 | (val.a, val.b) 82 | } 83 | } 84 | 85 | impl OrderedTuple2 { 86 | pub fn new(a: T, b: T) -> Self { 87 | let mut pair = (a, b); 88 | pair.sort_selection(); 89 | Self { 90 | a: pair.0, 91 | b: pair.1, 92 | } 93 | } 94 | 95 | pub fn other(&self, denom: &T) -> T { 96 | if &self.a == denom { 97 | self.b.clone() 98 | } else { 99 | self.a.clone() 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /docs/MANTIS CDA security via Composable Restaking.md: -------------------------------------------------------------------------------- 1 | # MANTIS CDA security via Composable Restaking 2 | 3 | MANTIS CDA has several roles: 4 | 5 | - Searchers (end user) 6 | - Sequencer 7 | - Blockbuilder 8 | - Relayer 9 | 10 | Composable Restaking has: 11 | - Staker (end user) 12 | - Validator 13 | - Fisherman 14 | - Relayer 15 | 16 | Relayer in both cases is same role. 17 | 18 | 19 | In this document is described some incetives and targets of MANTIS CDA roles, 20 | and how these can be aligned using Composable Restaking. 21 | 22 | TLRD; MANTIS CDA roles use Composable Restaking as slashable collateral and reputation system, and infrastucture to achieve both happen. 23 | 24 | 25 | ## MANTIS CDA Roles 26 | 27 | ### Searcher 28 | 29 | He finds arbitrage/MEV in some cross chain activity. He asks Sequencer to commit to execute cross chain what Searcher found, without leaking found opprotunity nor using for own profit. 30 | 31 | ### Sequencer 32 | 33 | Sequencer receives all found opportunites by Searchers and builds cross chain block. 34 | 35 | Sequencer asks Blockbuilders to commit for execution for parts of cross chain block. 36 | 37 | ### Relayer 38 | 39 | Sequencer asks Relayer to commit relaying some assets at specific order and specific block. 40 | 41 | ## Mechanics 42 | 43 | ## Slashing 44 | 45 | Sequencer slashd for failing Searcher execution, assuming he executed on hist own or just failed commitment. 46 | 47 | Blockbuilder/Relayer fail their SLA. 48 | 49 | ## Reputation 50 | 51 | MANTIS CDA roles allow to execute higher volume or more oftent if they have higher Stake delegated. 52 | 53 | When they slashed, Stakers redelegate stake to other entities. 54 | 55 | ## Composable Restaking Roles 56 | 57 | ## Fisherman 58 | 59 | Observe failures and reports them to Composable Restaking to Slash to MANTIS CDA Roles. 60 | 61 | Observer success and report it to Composable Restaking to unlock Rewards to MANTIS CDA Roles. 62 | 63 | ## Validators 64 | 65 | Verify Fisherman observation and slash/reward accordingly. 66 | 67 | ## What exactly Fisherman observes/reports and Validators verify? 68 | 69 | That to be defined in other document. -------------------------------------------------------------------------------- /docs/MANTIS Solvers and Composable Restaking.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This documents outlines how MANTIS Solver would use Composable Restaking to avoid rug users by providing misleading multi domain(multi block, multi chain, multi transaction) execution of wintents. 4 | 5 | Also it reflects on how CVM can be used over IBC to participate in slashing(fisherman+misbehaviour) protocol. 6 | 7 | 8 | ## Restaking 9 | 10 | Is: 11 | 12 | 1. Anystaking, contracts to allow stake/delegates anything(restake) 13 | 2. AVS - offchain components to execute integration with offchain protocols 14 | 3. Integration of 1 and 2, specifically slashing 15 | 16 | ## Solver 17 | 18 | Finds as set of cross chain routes expressed as CVM on chain program to execute user intent (for example, cross chain transfer and exchange). 19 | 20 | MANTIS exagarates problem because users are do not verify routes explicitly, like in SKIP ibc.fun and Paraswap. 21 | 22 | MANTIS Solver is AVS. 23 | 24 | ## Rug 25 | 26 | Multidomain nature of CVM solutions prevent their single block simulation, verification and reward. CVM can rug. 27 | 28 | Solution is Stake assigned to Solver which is Slashed in case of bad execution. 29 | 30 | ## Misbehaviour 31 | 32 | That is point 3 in the list. 33 | 34 | One way to is having set of validators observe domains and solution. 35 | 36 | In case of Solver solution fails to deliver promised settlement, validators 2/3 sign Slashing message of Solver Stake (collateral). 37 | 38 | Other way, is for fishermans to pemissionsly crank CVM execution of MANTIS outposts on each chain were solution to be settled within validated timeframe. 39 | 40 | CVM results in multihop propagation of failure prove up to Restaking chain. 41 | 42 | 43 | ## Notes 44 | 45 | Likely for various reasons validators 2/3 still will be needed to support MANTIS CVM based flow. 46 | 47 | 48 | Other operations like `Stake` and `Delegate` to support more Restaking flows are in Composable VM specification. 49 | 50 | Regardeless of solution, Validators and Indexers need unified realtime view of multi domain data to detect misbihaviours. -------------------------------------------------------------------------------- /docs/cda/orderflow-searchers-builders.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | title Maximizes IBC transfer privacy and exeuction speed 4 | 5 | node Solver { 6 | 7 | } 8 | 9 | node PrivateMevRelayer { 10 | collections UserTransactions 11 | collections IBCPacketsFromFromTransactions 12 | collections RoutesOfTransfersAndSwapsFromIBCPackets 13 | } 14 | 15 | node Centauri { 16 | file CosmosBeginOfBlock 17 | file CosmosMiddleOfBlock 18 | file CosmosEndOfBlock 19 | } 20 | 21 | node IBCRelayer { 22 | 23 | } 24 | 25 | node EthereumBlockSpaceAuction { 26 | 27 | } 28 | 29 | node IBCRelayer { 30 | 31 | } 32 | 33 | node Etherum { 34 | file EthereumBeginOfBlock 35 | file EthereumMiddleOfBlock 36 | file EthereumEndOfBlock 37 | } 38 | 39 | node SolanaBlockSpaceAuction { 40 | 41 | } 42 | 43 | node Solana { 44 | file SolanaBeginOfBlock 45 | file SolanaMiddleOfBlock 46 | file SolanaEndOfBlock 47 | } 48 | 49 | Solver -.-> PrivateMevRelayer : find suitable order 50 | 51 | PrivateMevRelayer -> CosmosEndOfBlock : put ordered transactions generating IBC packets into the end of block 52 | 53 | PrivateMevRelayer -.-> EthereumBlockSpaceAuction : book start of in future 54 | 55 | PrivateMevRelayer -.-> SolanaBlockSpaceAuction : book start of in future 56 | 57 | IBCRelayer -.-> EthereumBeginOfBlock : tx 58 | 59 | IBCRelayer -.-> SolanaBeginOfBlock : tx 60 | 61 | IBCRelayer -.-> Centauri : listen finalization from gossip 62 | 63 | IBCRelayer -.-> Centauri : grab reward for ordered delivery 64 | 65 | @enduml -------------------------------------------------------------------------------- /docs/mantis-blackbox-architecture.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | MANTIS Blackbox is algorithmic server doing some data mangling and number crunching to find routing solutions. 4 | 5 | Blackbox server depends on Simulation package. Simulation solve and verifies solution given existing data and heuristic constraints. 6 | 7 | Here is software diagram of interacting components. 8 | 9 | 10 | ```mermaid 11 | flowchart LR 12 | 13 | input((I want X for Y)) 14 | subgraph Blackbox 15 | input -- get indexer data --> raw[raw data] 16 | raw -- unify data to CVM format --> data 17 | subgraph Simulation 18 | data -- CS oracle --> od[oracalized data] 19 | od -- solve --> or[OR solver] 20 | od -- solve --> cs[CS solver] 21 | cs --> g[graph] 22 | g --> tg[traversed graph] 23 | tg --> route 24 | or -- scale in --> sd[scaled data] 25 | sd -- presolve --> ds[draft solution] 26 | ds -- solve --> fs[final solution] 27 | fs --> route 28 | route --> simulate 29 | simulate --> e{Evaluate} 30 | e --> |good solution or too many attempts| output 31 | e --> |bad| heuristic 32 | heuristic -- retry --> data 33 | end 34 | output((output)) 35 | end 36 | ``` 37 | 38 | Details of each step are outlined elsewhere in more low level places. -------------------------------------------------------------------------------- /docs/order/simple-mathemantics.md: -------------------------------------------------------------------------------- 1 | # Why solvers will not rug you? 2 | 3 | ## Let us have 4 orders 4 | 5 | 1. 100pica=10atom 6 | 2. 100pica=10atom 7 | 3. 20atom=100pica 8 | 4. 20atom=100pica 9 | 10 | Read it 100 pica for minumum for 10 atom and other direction. 11 | 12 | ### "Fair" solution is 13 | 14 | 1. 20atom 15 | 2. 20atom 16 | 3. 100pica 17 | 4. 100pica 18 | 19 | So read it `order number 1 got 20atom as he wanted` 20 | 21 | Let look different cases of "unfair". 22 | 23 | 24 | ### Limits 25 | 26 | ``` 27 | 1. 9atom 28 | ... 29 | ``` 30 | 31 | Solution giving first order 9 ATOM will fail, as it gives than limit wanted. 32 | 33 | So nobody will get less than limited, liked in FIFO Order Book. 34 | 35 | ### Not maximal volume 36 | 37 | 1. 10atom 38 | 2. 10atom 39 | 3. 100pica 40 | 4. 100pica 41 | 42 | So as you can see solver favored 3 and 4 solutions, but underfilled 1 and 2. 43 | 44 | Solution will be rejected. Why? 45 | 46 | Volume of `fair` solution is `40*200`. 47 | Volume of this solution is `20*200`. 48 | 49 | Solution with larger volume accepted. 50 | 51 | ### Not fair price 52 | 53 | 1. 10atom 54 | 2. 30atom 55 | 3. 100pica 56 | 4. 100pica 57 | 58 | In this case volume is good, but settling price for order 2 was better than order 3. 59 | 60 | This solution will be rejected, because all orders will be compare to same single accepted price. 61 | 1 and 2 violate one accepted price rule, so solution will be rejected. 62 | 63 | **CoWs ensure that users get fair prices against each other** 64 | 65 | ### Price is too far from oracle 66 | 67 | Also we do not use Oracle to define optimal price, 68 | but in case two solution are same in volume and fair price, 69 | solution which is closer to oracle is picked. 70 | 71 | ### Cross chain execution 72 | 73 | After users got fair price against each other in CoW, remaining part of batch can be solved cross chain. 74 | 75 | Cross chain execution uses MANTIS commitment protocol to ensure safe execution. 76 | 77 | ### Why batch CoW first and only equilibrium after? 78 | 79 | #### Market mechanics in equilibrium 80 | 81 | Like with FIFO Order Book like we all know, when Batch auctions has many solvers and many orders, limit difference narrows down and more solvers compete for being accepted, so larger volume. 82 | 83 | That leads to optimal spot price. 84 | 85 | We assume that fair price in batch auction eventually will be optimal, as safes gas costs of cross chain execution and generally more safe against MEV. 86 | 87 | So instead of these orders: 88 | ``` 89 | 1. 100pica=10atom 90 | 2. 100pica=10atom 91 | 3. 20atom=100pica 92 | 4. 20atom=100pica 93 | ``` 94 | 95 | we get these orders: 96 | ``` 97 | 1. 100pica=17atom 98 | 2. 101pica=18atom 99 | 3. 20atom=99pica 100 | 4. 21atom=100pica 101 | ``` 102 | -------------------------------------------------------------------------------- /mantis/.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "customizations": { 3 | "vscode": { 4 | "extensions": [ 5 | "mkhl.direnv" 6 | ] 7 | } 8 | }, 9 | "image": "ghcr.io/cachix/devenv:latest", 10 | "overrideCommand": false, 11 | "updateContentCommand": "devenv ci" 12 | } 13 | -------------------------------------------------------------------------------- /mantis/README.md: -------------------------------------------------------------------------------- 1 | ## poetry Setup 2 | 3 | ```sh 4 | poetry install 5 | poetry run blackbox 6 | ``` 7 | ## [pyright Installation](https://github.com/microsoft/pyright) 8 | 9 | ### Node Install 10 | 11 | If you don't have a recent version of node on your system, install that first from [nodejs.org](https://nodejs.org). 12 | 13 | Clear the npm cache: 14 | `npm cache clean -f` 15 | 16 | Install Node’s version manager: 17 | `npm install -g n` 18 | 19 | Finally, install the latest stable version 20 | `sudo n stable` 21 | 22 | ### pyright Package Installation 23 | #### Python Package 24 | A [community-maintained](https://github.com/RobertCraigie/pyright-python) Python package by the name of “pyright” is available on pypi and conda-forge. This package will automatically install node (which Pyright requires) and keep Pyright up to date. 25 | 26 | `pip install pyright` 27 | 28 | #### NPM Package 29 | Alternatively, you can install the command-line version of Pyright directly from npm, which is part of node. If you don't have a recent version of node on your system, install that first from [nodejs.org](https://nodejs.org). 30 | 31 | To install pyright globally: 32 | `npm install -g pyright` 33 | 34 | On MacOS or Linux, sudo may be required to install globally: 35 | `sudo npm install -g pyright` 36 | 37 | To update to the latest version: 38 | `sudo npm update -g pyright` -------------------------------------------------------------------------------- /mantis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/__init__.py -------------------------------------------------------------------------------- /mantis/blackbox/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # How indexer works 4 | 5 | It takes purely on chain data from registry from starter chain. 6 | 7 | Than it joins it with offchain static data about chains as defined in VCS repo. 8 | 9 | Than it gets data from available source about recent history summaries and current snapshots from running 3rd party systems. 10 | 11 | All that data is used to build oracles. 12 | 13 | Finally data is converted to simulator compatible data. -------------------------------------------------------------------------------- /mantis/blackbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/blackbox/__init__.py -------------------------------------------------------------------------------- /mantis/blackbox/custom_logging.py: -------------------------------------------------------------------------------- 1 | # Custom Logger Using Loguru 2 | 3 | import json 4 | import logging 5 | import sys 6 | from pathlib import Path 7 | 8 | from loguru import logger 9 | 10 | config_path = Path(__file__).with_name("logging_config.json") 11 | 12 | 13 | class InterceptHandler(logging.Handler): 14 | loglevel_mapping = { 15 | 50: "CRITICAL", 16 | 40: "ERROR", 17 | 30: "WARNING", 18 | 20: "INFO", 19 | 10: "DEBUG", 20 | 0: "NOTSET", 21 | } 22 | 23 | def emit(self, record): 24 | try: 25 | level = logger.level(record.levelname).name 26 | except AttributeError: 27 | level = self.loglevel_mapping[record.levelno] 28 | 29 | frame, depth = logging.currentframe(), 2 30 | while frame.f_code.co_filename == logging.__file__: 31 | frame = frame.f_back 32 | depth += 1 33 | 34 | log = logger.bind(request_id="app") 35 | log.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) 36 | 37 | 38 | class CustomizeLogger: 39 | @classmethod 40 | def make_logger(cls): 41 | config = cls.load_logging_config() 42 | logging_config = config.get("logger") 43 | 44 | logger = cls.customize_logging( 45 | logging_config.get("path"), 46 | level=logging_config.get("level"), 47 | retention=logging_config.get("retention"), 48 | rotation=logging_config.get("rotation"), 49 | format=logging_config.get("format"), 50 | ) 51 | return logger 52 | 53 | @classmethod 54 | def customize_logging(cls, filepath: Path, level: str, rotation: str, retention: str, format: str): 55 | logger.remove() 56 | logger.add(sys.stdout, enqueue=True, backtrace=True, level=level.upper(), format=format) 57 | logger.add( 58 | str(filepath), 59 | rotation=rotation, 60 | retention=retention, 61 | enqueue=True, 62 | backtrace=True, 63 | level=level.upper(), 64 | format=format, 65 | ) 66 | logging.basicConfig(handlers=[InterceptHandler()], level=0) 67 | logging.getLogger("uvicorn.access").handlers = [InterceptHandler()] 68 | for _log in ["uvicorn", "uvicorn.error", "fastapi"]: 69 | _logger = logging.getLogger(_log) 70 | _logger.handlers = [InterceptHandler()] 71 | 72 | return logger.bind(request_id=None, method=None) 73 | 74 | @classmethod 75 | def load_logging_config(cls): 76 | config = None 77 | with open(config_path) as config_file: 78 | config = json.load(config_file) 79 | return config 80 | 81 | 82 | logg = CustomizeLogger.make_logger() 83 | -------------------------------------------------------------------------------- /mantis/blackbox/cvm_runtime/__init__.py: -------------------------------------------------------------------------------- 1 | # generated by datamodel-codegen: 2 | # filename: raw 3 | -------------------------------------------------------------------------------- /mantis/blackbox/cvm_runtime/instantiate.py: -------------------------------------------------------------------------------- 1 | # generated by datamodel-codegen: 2 | # filename: instantiate.json 3 | 4 | from __future__ import annotations 5 | 6 | from pydantic import BaseModel, Field, RootModel, conint 7 | 8 | 9 | class Addr(RootModel[str]): 10 | root: str = Field( 11 | ..., 12 | description="A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", 13 | ) 14 | 15 | 16 | class NetworkId(RootModel[conint(ge=0)]): 17 | root: conint(ge=0) = Field( 18 | ..., 19 | description='Newtype for CVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.', 20 | ) 21 | 22 | 23 | class HereItem(BaseModel): 24 | admin: Addr = Field( 25 | ..., description='The admin which is allowed to update the bridge list.' 26 | ) 27 | network_id: NetworkId = Field( 28 | ..., description='Network ID of this network where contract is deployed' 29 | ) 30 | 31 | 32 | class InstantiateMsg(HereItem): 33 | pass 34 | -------------------------------------------------------------------------------- /mantis/blackbox/cvm_runtime/response_to_get_all_asset_venues.py: -------------------------------------------------------------------------------- 1 | # generated by datamodel-codegen: 2 | # filename: response_to_get_all_asset_venues.json 3 | 4 | from __future__ import annotations 5 | 6 | from enum import Enum 7 | from typing import List, Union 8 | 9 | from pydantic import BaseModel, ConfigDict, Field, RootModel 10 | 11 | 12 | class AssetId(RootModel[str]): 13 | root: str = Field( 14 | ..., 15 | description='Newtype for CVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.', 16 | ) 17 | 18 | 19 | class DisplayedForUint128(RootModel[str]): 20 | root: str = Field( 21 | ..., 22 | description='A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n```rust use cvm::shared::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#"{"value":"42"}"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#"{"value":"42"}"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```', 23 | ) 24 | 25 | 26 | class VenueId3(Enum): 27 | transfer = 'transfer' 28 | 29 | 30 | class VenueId4(BaseModel): 31 | model_config = ConfigDict( 32 | extra='forbid', 33 | ) 34 | exchange: DisplayedForUint128 35 | 36 | 37 | class VenueId(RootModel[Union[VenueId3, VenueId4]]): 38 | root: Union[VenueId3, VenueId4] 39 | 40 | 41 | class AssetsVenueItem(BaseModel): 42 | """ 43 | assets which can be transomed into each other via venue 44 | """ 45 | 46 | from_asset_id: AssetId 47 | to_asset_id: AssetId 48 | venue_id: VenueId 49 | 50 | 51 | class ArrayOfAssetsVenueItem(RootModel[List[AssetsVenueItem]]): 52 | root: List[AssetsVenueItem] = Field(..., title='Array_of_AssetsVenueItem') 53 | -------------------------------------------------------------------------------- /mantis/blackbox/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "asset_transfers": [ 4 | { 5 | "in_asset_id": "CENTAURI/ETHEREUM/USDC", 6 | "out_asset_id": "ETHEREUM/USDC", 7 | "usd_fee_transfer": 1000000, 8 | "in_token_amount": 100000, 9 | "out_token_amount": 100000, 10 | "fee_per_million": 0, 11 | "metadata": null 12 | }, 13 | { 14 | "in_asset_id": "CENTAURI/ETHEREUM/USDC", 15 | "out_asset_id": "OSMOSIS/CENTAURI/ETHEREUM/USDC", 16 | "usd_fee_transfer": 1, 17 | "in_token_amount": 100000, 18 | "out_token_amount": 100000, 19 | "fee_per_million": 0, 20 | "metadata": null 21 | }, 22 | { 23 | "in_asset_id": "OSMOSIS/ETHEREUM/USDC", 24 | "out_asset_id": "ETHEREUM/USDC", 25 | "usd_fee_transfer": 1, 26 | "in_token_amount": 100000, 27 | "out_token_amount": 100000, 28 | "fee_per_million": 0, 29 | "metadata": null 30 | } 31 | ], 32 | "asset_pairs_xyk": [ 33 | { 34 | "pool_id": 1, 35 | "in_asset_id": "ETHEREUM/USDC", 36 | "out_asset_id": "ETHEREUM/USDT", 37 | "fee_of_in_per_million": 0, 38 | "fee_of_out_per_million": 0, 39 | "weight_of_a": 1, 40 | "weight_of_b": 1, 41 | "pool_value_in_usd": 200000, 42 | "in_token_amount": 10000, 43 | "out_token_amount": 10000, 44 | "metadata": null 45 | }, 46 | { 47 | "pool_id": 2, 48 | "in_asset_id": "OSMOSIS/ETHEREUM/USDC", 49 | "out_asset_id": "OSMOSIS/ETHEREUM/USDT", 50 | "fee_of_in_per_million": 0, 51 | "fee_of_out_per_million": 0, 52 | "weight_of_a": 1, 53 | "weight_of_b": 1, 54 | "pool_value_in_usd": 200000, 55 | "in_token_amount": 10000, 56 | "out_token_amount": 10000, 57 | "metadata": null 58 | }, 59 | { 60 | "pool_id": 3, 61 | "in_asset_id": "CENTAURI/ETHEREUM/USDC", 62 | "out_asset_id": "CENTAURI/ETHEREUM/USDT", 63 | "fee_of_in_per_million": 0, 64 | "fee_of_out_per_million": 0, 65 | "weight_of_a": 1, 66 | "weight_of_b": 1, 67 | "pool_value_in_usd": 200000, 68 | "in_token_amount": 10000, 69 | "out_token_amount": 10000, 70 | "metadata": null 71 | }, 72 | { 73 | "pool_id": 4, 74 | "in_asset_id": "OSMOSIS/CENTAURI/ETHEREUM/USDC", 75 | "out_asset_id": "OSMOSIS/ETHEREUM/USDC", 76 | "fee_of_in_per_million": 0, 77 | "fee_of_out_per_million": 0, 78 | "weight_of_a": 1, 79 | "weight_of_b": 1, 80 | "pool_value_in_usd": 200000, 81 | "in_token_amount": 10000, 82 | "out_token_amount": 10000, 83 | "metadata": null 84 | } 85 | ], 86 | "usd_oracles": null 87 | }, 88 | "input": { 89 | "in_token_id": "CENTAURI/ETHEREUM/USDC", 90 | "out_token_id": "ETHEREUM/USDC", 91 | "in_amount": 1000, 92 | "out_amount": 100, 93 | "max": true 94 | } 95 | } -------------------------------------------------------------------------------- /mantis/blackbox/logging_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "logger": { 3 | "path": "./logs/mantis.log", 4 | "filename": "access.log", 5 | "level": "info", 6 | "rotation": "5 days", 7 | "retention": "1 months", 8 | "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} request id: {extra[request_id]} - {name}:{function} - {message}" 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /mantis/blackbox/neutron_pools.py: -------------------------------------------------------------------------------- 1 | # generated by datamodel-codegen: 2 | # filename: neutron_pools.json 3 | 4 | from __future__ import annotations 5 | 6 | from typing import List, Optional 7 | 8 | from pydantic import BaseModel, Field 9 | 10 | 11 | class Config(BaseModel): 12 | migrateToAddress: Optional[str] = None 13 | whitelisted: Optional[bool] = None 14 | 15 | 16 | class Prices(BaseModel): 17 | token1Address: str 18 | token1PriceUsd: float 19 | token2Address: str 20 | token2PriceUsd: float 21 | 22 | 23 | class Asset(BaseModel): 24 | id: str 25 | address: str 26 | amount: str 27 | symbol: str 28 | 29 | 30 | class AstroRewards(BaseModel): 31 | apr: float 32 | apy: int 33 | day: float 34 | 35 | 36 | class TotalRewards(BaseModel): 37 | apr: float 38 | apy: float 39 | day: float 40 | 41 | 42 | class TradingFees(BaseModel): 43 | apr: float 44 | apy: float 45 | day: float 46 | 47 | 48 | class Reward(BaseModel): 49 | symbol: str 50 | denom: str 51 | amountPerDay: str 52 | amountPerSecond: str 53 | priceUsd: float 54 | precision: int 55 | amountPerDayUsd: str 56 | yield_: float = Field(..., alias='yield') 57 | isExternal: bool 58 | gaugeInfo: List 59 | 60 | 61 | class JsonItem(BaseModel): 62 | chainId: str 63 | poolAddress: str 64 | lpAddress: str 65 | dayVolumeUsd: float 66 | poolLiquidityUsd: float 67 | poolLiquidity: int 68 | poolStakedLiquidityUsd: float 69 | poolStakedLiquidity: int 70 | config: Optional[Config] = None 71 | feeRate: List[str] 72 | poolType: str 73 | isBlocked: bool 74 | prices: Prices 75 | stakeable: bool 76 | assets: List[Asset] 77 | name: str 78 | isNew: bool 79 | isIlliquid: bool 80 | isDeregistered: bool 81 | astroRewards: AstroRewards 82 | totalRewards: TotalRewards 83 | tradingFees: TradingFees 84 | rewards: List[Reward] 85 | 86 | 87 | class Model(BaseModel): 88 | json_: List[JsonItem] = Field(..., alias='json') 89 | -------------------------------------------------------------------------------- /mantis/blackbox/osmosis_pools.py: -------------------------------------------------------------------------------- 1 | # generated by datamodel-codegen: 2 | # filename: osmosis_pools.json 3 | 4 | from __future__ import annotations 5 | 6 | from typing import List, Optional 7 | 8 | from pydantic import BaseModel, Field, RootModel 9 | 10 | 11 | class PoolParams(BaseModel): 12 | exit_fee: str 13 | swap_fee: str 14 | smooth_weight_change_params: None 15 | 16 | 17 | class TotalShares(BaseModel): 18 | denom: str 19 | amount: str 20 | 21 | 22 | class PoolLiquidityItem(BaseModel): 23 | denom: str 24 | amount: str 25 | 26 | 27 | class Token(BaseModel): 28 | denom: str 29 | amount: str 30 | 31 | 32 | class PoolAsset(BaseModel): 33 | token: Token 34 | weight: str 35 | 36 | 37 | class ModelItem(BaseModel): 38 | field_type: str = Field(..., alias='@type') 39 | id: Optional[str] = None 40 | pool_params: Optional[PoolParams] = None 41 | total_shares: Optional[TotalShares] = None 42 | liquidityUsd: float 43 | liquidity24hUsdChange: Optional[float] = None 44 | volume24hUsd: float 45 | volume24hUsdChange: Optional[float] = None 46 | volume7dUsd: float 47 | taker_fee: str 48 | pool_liquidity: Optional[List[PoolLiquidityItem]] = None 49 | scaling_factors: Optional[List[str]] = None 50 | scaling_factor_controller: Optional[str] = None 51 | pool_assets: Optional[List[PoolAsset]] = None 52 | total_weight: Optional[str] = None 53 | address: Optional[str] = None 54 | current_tick_liquidity: Optional[str] = None 55 | token0: Optional[str] = None 56 | token0Amount: Optional[str] = None 57 | token1: Optional[str] = None 58 | token1Amount: Optional[str] = None 59 | current_sqrt_price: Optional[str] = None 60 | current_tick: Optional[str] = None 61 | tick_spacing: Optional[str] = None 62 | exponent_at_price_one: Optional[str] = None 63 | spread_factor: Optional[str] = None 64 | pool_id: Optional[str] = None 65 | code_id: Optional[str] = None 66 | tokens: Optional[List[Token]] = None 67 | 68 | 69 | class Model(RootModel[List[ModelItem]]): 70 | root: List[ModelItem] 71 | -------------------------------------------------------------------------------- /mantis/blackbox/raw.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from pydantic import BaseModel 4 | 5 | from blackbox.composablefi_networks import Model as NetworksModel 6 | from blackbox.cvm_runtime.response_to_get_config import GetConfigResponse as CvmRegistry 7 | from blackbox.neutron_pools import Model as NeutronPoolsModel 8 | from blackbox.osmosis_pools import Model as OsmosisPoolsModel 9 | from blackbox.skip_money import Chain 10 | 11 | 12 | class OsmosisPoolsResponse(BaseModel): 13 | pools: OsmosisPoolsModel 14 | 15 | 16 | class CosmosChains(BaseModel): 17 | chains: list[Chain] 18 | 19 | 20 | class NeutronPoolsResponseData(BaseModel): 21 | data: NeutronPoolsModel 22 | 23 | 24 | class NeutronPoolsResponse(BaseModel): 25 | result: NeutronPoolsResponseData 26 | 27 | 28 | class AllData(BaseModel): 29 | astroport_pools: Union[NeutronPoolsModel, None] 30 | osmosis_pools: Union[OsmosisPoolsModel, None] 31 | cosmos_chains: CosmosChains 32 | cvm_registry: CvmRegistry 33 | networks: NetworksModel 34 | -------------------------------------------------------------------------------- /mantis/blackbox/settings.py: -------------------------------------------------------------------------------- 1 | from pydantic import Field 2 | from pydantic_settings import BaseSettings 3 | 4 | 5 | class Settings(BaseSettings): 6 | OSMOSIS_POOLS: str | None = Field(alias="OSMOSIS_POOLS", default="http://app.osmosis.zone/api/pools") 7 | CVM_COSMOS_GRPC: str | None = Field(alias="CVM_COSMOS_GRPC", default="https://osmosis.grpc.stakin-nodes.com:443") 8 | CVM_CHAIN_ID: str | None = Field(alias="CVM_CHAIN_ID", default="osmosis-1") 9 | COMPOSABLEFI_NETWORKS: str | None = Field( 10 | alias="COMPOSABLEFI_NETWORKS", 11 | default="https://raw.githubusercontent.com/ComposableFi/networks/main/networks.autogenerated.json", 12 | ) 13 | CVM_CHAIN_FEE: str | None = Field(alias="CVM_CHAIN_FEE", default="uosmo") 14 | 15 | cvm_address: str | None = Field( 16 | alias="CVM_ADDRESS", 17 | default="osmo13guwqtt7xdcuhtewc53tpt9jas5xcnk5tvzdxwhn09774m8jpytqr89pry", 18 | ) 19 | astroport_pools: str | None = Field(alias="ASTROPORT_POOLS", default="https://app.astroport.fi/api/trpc/pools.getAll?input=%7B%22json%22%3A%7B%22chainId%22%3A%5B%22neutron-1%22%5D%7D%7D") 20 | neutron_rpc: str | None = Field(alias="NEUTRON_RPC", default=None) 21 | osmosis_rpc: str | None = Field(alias="OSMOSIS_RPC", default=None) 22 | skip_money: str | None = Field(alias="SKIP_MONEY", default="https://api.skip.money/") 23 | port: int = Field(default=8000, alias="LISTEN_PORT") 24 | 25 | 26 | settings = Settings() 27 | -------------------------------------------------------------------------------- /mantis/blackbox/test_joiner.py: -------------------------------------------------------------------------------- 1 | from blackbox.cvm_runtime.execute import ( 2 | AssetId, 3 | AssetItem, 4 | AssetReference, 5 | AssetReference1, 6 | Native, 7 | NetworkId, 8 | ) 9 | 10 | 11 | def test_bases(): 12 | AssetItem( 13 | asset_id=AssetId("42"), 14 | local=AssetReference(AssetReference1(native=Native(denom="osmo"))), 15 | network_id=NetworkId("2"), 16 | ) 17 | AssetItem( 18 | asset_id=AssetId("13"), 19 | local=AssetReference(AssetReference1(native=Native(denom="pica"))), 20 | network_id=NetworkId("3"), 21 | ) 22 | -------------------------------------------------------------------------------- /mantis/blackbox_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackbox_rs" 3 | version = "3.0.3" 4 | edition = "2021" 5 | license = "SPECIFY A LICENSE BEFORE PUBLISHING" 6 | 7 | [dependencies] 8 | bytes = "1.5.0" 9 | futures-core = "0.3.30" 10 | progenitor-client = "0.6.0" 11 | reqwest = { version = "0.11.24", default-features=false, features = ["json", "stream"] } 12 | serde = { version = "1.0.197", features = ["derive"] } 13 | serde_urlencoded = "0.7.1" 14 | 15 | -------------------------------------------------------------------------------- /mantis/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | poetry run pytest -s 3 | poetry run ruff check . --exit-non-zero-on-fix --unsafe-fixes --config pyproject.toml 4 | poetry build -vvv 5 | poetry run blackbox/ -vvv & 6 | sleep 5; curl -v -X 'GET' 'http://0.0.0.0:8000/status' -H 'accept: application/json' 7 | pkill blackbox 8 | poetry check --lock -------------------------------------------------------------------------------- /mantis/executor/README.md: -------------------------------------------------------------------------------- 1 | # MANTIS Executor 2 | 3 | Permissionless cranker and propagator of CVM programs. 4 | 5 | Used to help to facilitate multi block (multi transaction) execution of single CVM program. -------------------------------------------------------------------------------- /mantis/fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | poetry lock --no-update 4 | poetry install 5 | poetry run ruff format . --config pyproject.toml 6 | poetry run ruff . --exit-non-zero-on-fix --config pyproject.toml --fix -------------------------------------------------------------------------------- /mantis/node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mantis-node" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "mantis" 8 | 9 | [dependencies] 10 | blackbox_rs = { path = "./../blackbox_rs" } 11 | bip32 = { workspace = true, default-features = false, features = [ 12 | "alloc", 13 | "secp256k1", 14 | "mnemonic", 15 | "bip39", 16 | ] } 17 | bip39 = {version = "2.0.0", features = ["std"]} 18 | clap = { workspace = true, features = ["derive", "std"] } 19 | cosmos-sdk-proto = { workspace = true, features = ["grpc-transport", "cosmwasm", "grpc"] } 20 | cosmrs = { workspace = true, features = []} 21 | cosmwasm-std = { workspace = true, features = []} 22 | cw-mantis-order = { workspace = true, features = [ 23 | "json-schema", 24 | "std", 25 | ] } 26 | 27 | displaydoc = { workspace = true } 28 | cw-cvm-outpost = { workspace = true, features = ["std"] } 29 | cw-cvm-executor = { workspace = true, features = ["std"] } 30 | 31 | mantis-cw = { workspace = true, default-features = false } 32 | log = { workspace = true } 33 | derive_more = { workspace = true, features = [] } 34 | itertools = { workspace = true, features = []} 35 | num-traits = { workspace = true, features = ["std"]} 36 | prost-types = { workspace = true } 37 | rand = { workspace = true, default-features = false } 38 | rand_distr = { workspace = true } 39 | rust_decimal = { workspace = true } 40 | rust_decimal_macros = { workspace = true } 41 | serde = { workspace = true, features = ["derive", "std"] } 42 | serde-json-wasm = { workspace = true } 43 | strum = { workspace = true } 44 | strum_macros = { workspace = true } 45 | tokio = { workspace = true, features = ["full"] } 46 | tonic = { workspace = true } 47 | tuples = { workspace = true } 48 | cvm-runtime = { workspace = true, features = ["std"] } 49 | cvm-route ={ workspace = true} 50 | ndarray ={ workspace = true, features = ["std", "serde"]} 51 | env_logger = {workspace = true} 52 | petgraph = {workspace = true} 53 | num-rational = {workspace = true, features = ["cosmwasm", "serde", "json-schema"], default-features = false} 54 | sha2 = {workspace = true} 55 | 56 | bounded-collections = {workspace = true, default-features = false, features = ["std"]} 57 | 58 | [dev-dependencies] 59 | cw-multi-test = { workspace = true } 60 | cw-orch = { workspace = true} 61 | rustfmt-wrapper = "0.2.1" 62 | serde_json = { workspace = true } -------------------------------------------------------------------------------- /mantis/node/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cargo progenitor --input=../../schema/mantis_solver_blackbox.json --output=blackbox_rs --name=blackbox_rs --version="3.0.3" -------------------------------------------------------------------------------- /mantis/node/src/README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | 4 | Blackbox never calls any transactions, because it is need higher level of security and because TX clients are binary highly tuned to network, to which Python may not hae good access. 5 | 6 | 7 | ## Integration 8 | 9 | ### Dependencies 10 | 11 | All Python facilities (including data mangling) are done in `../simulation` codebase. 12 | 13 | `../../crates/cvm` and `../../crates/mantis` provide JSON schemas for relevant data. 14 | 15 | Use schemas from Neutron/Skip/Osmosis as needed. 16 | 17 | Code as simples web service most popular in data science people named FastAPI. 18 | 19 | ### Data 20 | 21 | Server uses Rust JSON Schema data types for CVM/MANTIS contracts when available. 22 | 23 | Python calls off chain APIs for Osmosis/Neutron about Pools. And SKIP API about relays using their schemas. 24 | 25 | No heavy lifting of data. Just simple gets. 26 | 27 | This code never simulates transactions, all such input provided inside call. 28 | 29 | Blackbox is not interactive regarding transactions. 30 | 31 | I doubt https://github.com/fetchai/cosmpy is good to get data as it lacks all shapes, so solution will be if need very specific direct chain data(rare), will talk to indexer team or spawn simple mantis-data (written in cosmrs) server to fetch it. But I hope it will not be needed. 32 | 33 | ### Sandbox 34 | 35 | All data getting to be here. So that research can get current state of data just immediately to run numerics and debug. 36 | 37 | All calls are hidden behind `data` function. Including `../../routing.md` data (about routes possible). 38 | 39 | Data-faced maps Cosmos assets and data to CVM/MANTIS data, and mangles it to provide 40 | 41 | Server hosted via https://github.com/ComposableFi/env with full access of relevant people. 42 | 43 | Server works in constant restart in case of failure. 44 | 45 | Server fails on any overflow or undecided input, so it can be logged and fixed. -------------------------------------------------------------------------------- /mantis/node/src/bin/simulator.rs: -------------------------------------------------------------------------------- 1 | use mantis_cw::OrderSide; 2 | use mantis_node::solver::cows::{orderbook::OrderList, solution::Solution}; 3 | use mantis_node::solver::types::SolverOrder; 4 | use mantis_node::{prelude::*, solver::types::Price}; 5 | 6 | fn main() { 7 | // decide on basics 8 | let order_a = SolverOrder::new_integer(100_000, 3, OrderSide::B, 1); 9 | let order_b = SolverOrder::new_integer(3, 100_000, OrderSide::A, 2); 10 | assert!(order_a.is_acceptable_price(order_b.limit_price)); 11 | assert!(order_b.is_acceptable_price(order_a.limit_price)); 12 | 13 | let ab = OrderList { 14 | value: vec![order_a, order_b], 15 | }; 16 | ab.print(); 17 | let optimal_price = ab.compute_optimal_price(50); 18 | 19 | let mut solution = Solution::new(ab.value.clone()); 20 | solution = solution.match_orders(optimal_price); 21 | solution.print(); 22 | 23 | println!( 24 | "{:}", 25 | solution.orders.value[0] 26 | .amount_filled 27 | .to_u128() 28 | .expect("msg") 29 | ); 30 | println!( 31 | "{:}", 32 | solution.orders.value[1] 33 | .amount_filled 34 | .to_u128() 35 | .expect("msg") 36 | ); 37 | 38 | // randomize price around 2.0 (ratio of 2 price tokens in pair) 39 | let orders = (1..100).map(|x| SolverOrder::random_f64(2., 0.1, (50, 150), x)); 40 | let orders = OrderList { 41 | value: orders.collect(), 42 | }; 43 | orders.print(); 44 | 45 | // solves nothing as no really overlap of orders 46 | let mut solution = Solution::new(orders.value.clone()); 47 | solution = solution.match_orders(Price::new_float(1.0)); 48 | solution.print(); 49 | 50 | // solves for some specific price some 51 | let mut solution = Solution::new(orders.value.clone()); 52 | solution = solution.match_orders(Price::new_float(2.05)); 53 | solution.print(); 54 | 55 | // finds maximal volume price 56 | let mut solution = Solution::new(orders.value.clone()); 57 | solution = solution.match_orders(optimal_price); 58 | solution.print(); 59 | 60 | // big simulation 61 | let optimal_price = orders.compute_optimal_price(50); 62 | 63 | // fill orders 64 | let mut solution = Solution::new(orders.value.clone()); 65 | solution = solution.match_orders(optimal_price); 66 | solution.print(); 67 | } 68 | -------------------------------------------------------------------------------- /mantis/node/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::Uint128; 2 | 3 | #[derive(Debug, displaydoc::Display)] 4 | pub enum MantisError { 5 | /// `{order_id}` Matching order not found 6 | MatchingOrderNotFound { order_id: Uint128 }, 7 | /// `{order_id}` Cow fill badly found because `{reason}` 8 | CowFillBadlyFound { order_id: Uint128, reason: String }, 9 | /// Blackbox error: `{reason}` 10 | BlackboxError { reason: String }, 11 | /// `{source}` Failed to broadcast tx 12 | FailedToBroadcastTx { source: String }, 13 | /// `{source}` Failed to execute tx 14 | FailedToExecuteTx { source: String }, 15 | } 16 | 17 | impl std::error::Error for MantisError {} 18 | -------------------------------------------------------------------------------- /mantis/node/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(let_chains)] 2 | pub mod error; 3 | pub mod mantis; 4 | pub mod prelude; 5 | pub mod solver; 6 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/autopilot.rs: -------------------------------------------------------------------------------- 1 | use cosmrs::{tx::Msg, Gas}; 2 | 3 | use crate::{ 4 | mantis::cosmos::{ 5 | client::{simulate_and_set_fee, tx_broadcast_single_signed_msg}, 6 | cosmwasm::to_exec_signed, 7 | }, 8 | prelude::*, 9 | }; 10 | 11 | use super::cosmos::client::{CosmWasmWriteClient, CosmosChainInfo, CosmosQueryClient, Tip}; 12 | 13 | pub async fn cleanup( 14 | _write_client: &mut CosmWasmWriteClient, 15 | _cosmos_query_client: &mut CosmosQueryClient, 16 | order_contract: String, 17 | signing_key: &cosmrs::crypto::secp256k1::SigningKey, 18 | rpc: &CosmosChainInfo, 19 | tip: &Tip, 20 | gas: Gas, 21 | ) { 22 | log::debug!(target: "mantis::autopilot", " cleanup of old orders"); 23 | let auth_info = simulate_and_set_fee(signing_key, &tip.account, gas).await; 24 | let msg = cw_mantis_order::ExecMsg::Timeout { 25 | orders: vec![], 26 | solutions: vec![], 27 | }; 28 | let msg = to_exec_signed(signing_key, order_contract, msg); 29 | let result = tx_broadcast_single_signed_msg( 30 | msg.to_any().expect("proto"), 31 | auth_info, 32 | rpc, 33 | signing_key, 34 | tip, 35 | ) 36 | .await 37 | .expect("cleaned"); 38 | match &result.tx_result.code { 39 | cosmrs::tendermint::abci::Code::Err(err) => { 40 | log::error!("clean result: {:?}", result); 41 | } 42 | cosmrs::tendermint::abci::Code::Ok => { 43 | log::trace!("ok: {:?}", result); 44 | } 45 | } 46 | } 47 | 48 | /// move protocol forwards, cranks auctions ending and also cleans up old orders 49 | async fn _move() {} 50 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/cosmos/cosmwasm.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use cosmos_sdk_proto::cosmwasm::{self, wasm::v1::QuerySmartContractStateRequest}; 4 | use cosmrs::{cosmwasm::MsgExecuteContract, AccountId}; 5 | use cosmwasm_std::Coin; 6 | 7 | pub fn to_exec_signed_with_fund( 8 | signing_key: &cosmrs::crypto::secp256k1::SigningKey, 9 | order_contract: String, 10 | msg: T, 11 | fund: cosmrs::Coin, 12 | ) -> MsgExecuteContract { 13 | let funds = vec![fund]; 14 | to_exec_signed_with_funds(signing_key, order_contract, msg, funds) 15 | } 16 | 17 | pub fn to_exec_signed( 18 | signing_key: &cosmrs::crypto::secp256k1::SigningKey, 19 | order_contract: String, 20 | msg: T, 21 | ) -> MsgExecuteContract { 22 | to_exec_signed_with_funds(signing_key, order_contract, msg, vec![]) 23 | } 24 | 25 | pub fn to_exec_signed_with_funds( 26 | signing_key: &cosmrs::crypto::secp256k1::SigningKey, 27 | order_contract: String, 28 | msg: T, 29 | funds: Vec, 30 | ) -> MsgExecuteContract { 31 | let msg = MsgExecuteContract { 32 | sender: signing_key 33 | .public_key() 34 | .account_id("centauri") 35 | .expect("account"), 36 | contract: AccountId::from_str(&order_contract).expect("contract"), 37 | msg: serde_json_wasm::to_vec(&msg).expect("json"), 38 | funds, 39 | }; 40 | msg 41 | } 42 | 43 | pub async fn smart_query( 44 | order_contract: &String, 45 | query: T, 46 | read: &mut cosmwasm::wasm::v1::query_client::QueryClient, 47 | ) -> O { 48 | let orders_request = QuerySmartContractStateRequest { 49 | address: order_contract.clone(), 50 | query_data: serde_json_wasm::to_vec(&query).expect("json"), 51 | }; 52 | let result = read 53 | .smart_contract_state(orders_request) 54 | .await 55 | .expect("smart_query failed") 56 | .into_inner() 57 | .data; 58 | serde_json_wasm::from_slice(&result).expect("result parsed") 59 | } 60 | 61 | pub fn parse_coin_pair(pair: &String) -> (Coin, Coin) { 62 | let pair: Vec<_> = pair 63 | .split(',') 64 | .map(|x| Coin::from_str(x).expect("coin")) 65 | .collect(); 66 | assert_eq!(pair.len(), 2); 67 | (pair[0].clone(), pair[1].clone()) 68 | } 69 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/cosmos/cvm.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read; 2 | 3 | use mantis_cw::DenomPair; 4 | 5 | use super::client::Tip; 6 | 7 | /// given key and latest block and sequence number, produces binary salt for cross chain block 8 | /// isolating execution of one cross chain transaction from other 9 | pub fn calculate_salt( 10 | signing_key: &cosmrs::crypto::secp256k1::SigningKey, 11 | tip: &Tip, 12 | pair: DenomPair, 13 | ) -> Vec { 14 | use sha2::{Digest, Sha224}; 15 | let mut base = signing_key.public_key().to_bytes().to_vec(); 16 | base.extend(tip.block.value().to_be_bytes().to_vec()); 17 | base.extend(tip.account.sequence.to_be_bytes().to_vec()); 18 | base.extend(pair.a.as_bytes().to_vec()); 19 | base.extend(pair.b.as_bytes().to_vec()); 20 | let mut hasher = Sha224::default(); 21 | hasher.update(base); 22 | hasher.finalize().into_iter().take(16).collect() 23 | } 24 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/cosmos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod cosmwasm; 3 | pub mod cvm; 4 | pub mod signer; 5 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/cosmos/signer.rs: -------------------------------------------------------------------------------- 1 | //! given whatever string, give me the signer struct 2 | 3 | use cosmrs::crypto::secp256k1::SigningKey; 4 | 5 | pub fn from_mnemonic(phrase: &str, derivation_path: &str) -> Result { 6 | let seed = bip39::Mnemonic::parse_normalized(phrase) 7 | .expect("parsed") 8 | .to_seed_normalized(""); 9 | 10 | let xprv = bip32::XPrv::derive_from_path(seed, &derivation_path.parse().expect("parse")) 11 | .expect("derived"); 12 | let signer_priv: SigningKey = xprv.into(); 13 | 14 | Ok(signer_priv) 15 | } 16 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/indexer.rs: -------------------------------------------------------------------------------- 1 | use cvm_runtime::outpost::GetConfigResponse; 2 | use cw_mantis_order::OrderItem; 3 | 4 | use crate::mantis::cosmos::cosmwasm::smart_query; 5 | 6 | use super::cosmos::client::{CosmWasmReadClient, Tip}; 7 | 8 | pub async fn get_active_orders( 9 | order_contract: &String, 10 | cosmos_query_client: &mut CosmWasmReadClient, 11 | tip: &Tip, 12 | ) -> Vec { 13 | let query = cw_mantis_order::QueryMsg::GetAllOrders {}; 14 | smart_query::<_, Vec>(order_contract, query, cosmos_query_client) 15 | .await 16 | .into_iter() 17 | .filter(|x| x.msg.timeout > tip.block.value()) 18 | .collect::>() 19 | } 20 | 21 | pub async fn get_stale_orders( 22 | order_contract: &String, 23 | cosmos_query_client: &mut CosmWasmReadClient, 24 | tip: &Tip, 25 | ) -> Vec { 26 | let query = cw_mantis_order::QueryMsg::GetAllOrders {}; 27 | smart_query::<_, Vec>(order_contract, query, cosmos_query_client) 28 | .await 29 | .into_iter() 30 | .filter(|x| x.msg.timeout < tip.block.value()) 31 | .collect::>() 32 | } 33 | 34 | pub async fn has_stale_orders( 35 | order_contract: &String, 36 | cosmos_query_client: &mut CosmWasmReadClient, 37 | tip: &Tip, 38 | ) -> bool { 39 | let query = cw_mantis_order::QueryMsg::HasStale {}; 40 | smart_query::<_, bool>(order_contract, query, cosmos_query_client).await 41 | } 42 | 43 | pub async fn get_cvm_glt( 44 | contract: &String, 45 | cosmos_query_client: &mut CosmWasmReadClient, 46 | ) -> GetConfigResponse { 47 | let query = cvm_runtime::outpost::QueryMsg::GetConfig {}; 48 | smart_query::<_, GetConfigResponse>(contract, query, cosmos_query_client).await 49 | } 50 | -------------------------------------------------------------------------------- /mantis/node/src/mantis/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod args; 2 | pub mod autopilot; 3 | pub mod blackbox; 4 | pub mod cosmos; 5 | pub mod indexer; 6 | pub mod simulate; 7 | pub mod solve; 8 | -------------------------------------------------------------------------------- /mantis/node/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! just make all math/containers/number at hand. for now i concern to be as close as possible to the original python code in simplicity. 2 | pub use itertools::*; 3 | pub use log::{debug, error, info, trace, warn}; 4 | pub use num_traits::cast::ToPrimitive; 5 | pub use rand::distributions::Standard; 6 | pub use rand::prelude::*; 7 | pub use rand::Rng; 8 | pub use rand_distr::{Distribution, Normal}; 9 | pub use rust_decimal::Decimal; 10 | pub use rust_decimal_macros::dec; 11 | pub use std::cmp::max; 12 | pub use std::cmp::min; 13 | pub use std::cmp::Ordering; 14 | pub use std::collections::HashMap; 15 | pub use std::fmt::format; 16 | pub use std::fmt::Debug; 17 | pub use std::str::FromStr; 18 | pub use std::vec; 19 | pub use tuples::*; 20 | 21 | #[cfg(test)] 22 | pub use cosmwasm_std::testing::*; 23 | -------------------------------------------------------------------------------- /mantis/node/src/solver/cows/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod optimizer; 2 | pub mod orderbook; 3 | pub mod solution; 4 | -------------------------------------------------------------------------------- /mantis/node/src/solver/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cows; 2 | pub mod router; 3 | pub mod types; 4 | -------------------------------------------------------------------------------- /mantis/node/src/solver/router/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod shortest_path; 2 | -------------------------------------------------------------------------------- /mantis/node/src/solver/router/optimal_routing.rs: -------------------------------------------------------------------------------- 1 | use itertools::*; 2 | use mantis_node::solver::router::*; 3 | use std::collections::HashMap; 4 | use tuples::TupleCloned; 5 | fn main() { 6 | let center_node = "CENTAURI"; 7 | 8 | let mut chains: HashMap> = HashMap::new(); 9 | chains.insert( 10 | "ETHEREUM".to_owned(), 11 | vec!["WETH".to_owned(), "USDC".to_owned(), "SHIBA".to_owned()], 12 | ); 13 | chains.insert(center_node.to_owned(), vec![]); 14 | chains.insert( 15 | "OSMOSIS".to_owned(), 16 | vec!["ATOM".to_owned(), "SCRT".to_owned()], 17 | ); 18 | 19 | populate_chain_dict(&mut chains, center_node.to_owned()); 20 | println!("{:?}", chains); 21 | 22 | let mut all_tokens = vec![]; 23 | let mut all_cfmms = vec![]; 24 | let mut reserves = vec![]; 25 | let mut cfmm_tx_cost = vec![]; 26 | for (other_chain, other_tokens) in chains.clone() { 27 | all_tokens.extend(other_tokens.clone()); 28 | let cfmms = other_tokens 29 | .clone() 30 | .into_iter() 31 | .combinations(2) 32 | .map(|x| (x[0].clone(), x[1].clone())); 33 | all_cfmms.extend(cfmms); 34 | } 35 | use rand::prelude::*; 36 | let tx_costs_random = rand_distr::Uniform::new(0, 20); 37 | let reserves_radom = rand_distr::Uniform::new(9500, 10051); 38 | for cfmm in all_cfmms.iter() { 39 | let a = reserves_radom.sample(&mut rand::thread_rng()); 40 | let b = reserves_radom.sample(&mut rand::thread_rng()); 41 | reserves.push((a, b)); 42 | let value = tx_costs_random.sample(&mut rand::thread_rng()); 43 | cfmm_tx_cost.push(value); 44 | } 45 | 46 | let mut ibc_pools = 0u32; 47 | let tx_costs_random = rand_distr::Uniform::new(0, 20); 48 | let reserves_random = rand_distr::Uniform::new(10000, 11000); 49 | for token_on_center in chains.get(center_node).unwrap() { 50 | for (other_chain, other_tokens) in chains.iter() { 51 | if other_chain != center_node { 52 | for other_token in other_tokens { 53 | if token_on_center.contains(other_token) 54 | || other_token.contains(token_on_center) 55 | { 56 | all_cfmms.push((token_on_center.to_owned(), other_token.to_owned())); 57 | let a = reserves_random.sample(&mut rand::thread_rng()); 58 | let b = reserves_random.sample(&mut rand::thread_rng()); 59 | reserves.push((a, b)); 60 | cfmm_tx_cost.push(tx_costs_random.sample(&mut rand::thread_rng())); 61 | ibc_pools += 1; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | let mut fees = vec![]; 69 | let fees_random = rand_distr::Uniform::new(0.97, 0.999); 70 | for cfmm in 0..all_cfmms.len() { 71 | let value = fees_random.sample(&mut rand::thread_rng()); 72 | fees.push(value); 73 | } 74 | 75 | println!("{:?}", reserves); 76 | 77 | for item in all_tokens.iter().enumerate() { 78 | println!("{:?}", item); 79 | } 80 | 81 | for item in all_cfmms.iter().enumerate() { 82 | println!("{:?}", item); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /mantis/node/src/solver/router/or.rs: -------------------------------------------------------------------------------- 1 | pub mod bf; 2 | 3 | const MIN_RESERVE: f64 = 05000.0; 4 | const MAX_RESERVE: f64 = 15000.0; 5 | 6 | use std::{ 7 | collections::HashMap, 8 | ops::{AddAssign, Index}, 9 | }; 10 | 11 | use cosmrs::tendermint::chain; 12 | use ndarray::*; 13 | use prelude::*; 14 | use tuples::TupleCloned; 15 | 16 | pub fn populate_chain_dict(chains: &mut HashMap>, center_node: String) { 17 | let others: HashMap<_, _> = chains 18 | .clone() 19 | .into_iter() 20 | .filter(|(chain, _)| *chain != center_node) 21 | .collect(); 22 | 23 | for (chain, tokens) in others.iter() { 24 | let mut center = chains.entry(center_node.clone()); 25 | let center = center.or_insert(<_>::default()); 26 | for token in tokens { 27 | center.push(format!("{}/{}", chain, token)) 28 | } 29 | } 30 | 31 | let center_tokens = chains.get(¢er_node).unwrap().clone(); 32 | 33 | for (chain, tokens) in chains.iter_mut() { 34 | if chain != ¢er_node { 35 | for token in center_tokens.iter() { 36 | if !token.contains(chain) { 37 | tokens.push(format!("{}/{}", center_node, token)); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | // pub fn solve( 45 | // all_tokens: Vec, 46 | // all_cffms: Vec<(String, String)>, 47 | // reserves: ndarray::Array1, 48 | // cfmm_tx_cost: Vec, 49 | // fees: Vec, 50 | // ibc_pools: u16, 51 | // origin_token: String, 52 | // number_of_init_tokens: f64, 53 | // obj_token: String, 54 | // force_eta: Vec, 55 | // ) { 56 | // let count_tokens = all_tokens.len(); 57 | // let count_cffms = all_cffms.len(); 58 | // let mut current_assets = ndarray::Array1::::from_elem(count_tokens, <_>::default()); 59 | 60 | // fn find_index(vec: &[T], target: &T) -> usize { 61 | // vec.iter().position(|x| x == target).unwrap() 62 | // } 63 | // current_assets[find_index(&all_tokens, &origin_token)] = number_of_init_tokens; 64 | 65 | // let mut problem = ProblemVariables::new(); 66 | // let mut main_expression = Expression::default(); 67 | // let mut constraints: Vec = vec![]; 68 | 69 | // // Build variables 70 | 71 | // let mut variables = variables!(); 72 | 73 | // let mut A: Vec>> = vec![]; 74 | 75 | // for cfmm in all_cffms.iter() { 76 | // let n_i = 2; 77 | // let mut A_i = vec![vec![0.0, 0.0]; count_tokens]; 78 | // A_i[find_index(&all_tokens, &cfmm.0)][0] = 1.0; 79 | // A_i[find_index(&all_tokens, &cfmm.1)][1] = 1.0; 80 | // A.push(A_i); 81 | // } 82 | 83 | // let mut deltas: Vec> = vec![]; 84 | // let mut lambdas: Vec> = vec![]; 85 | // // Binary value, indicates tx or not for given pool 86 | // let mut eta: Vec = vec![]; 87 | 88 | // for _ in all_cffms { 89 | // deltas.push(vec![variable(), variable()]); 90 | // lambdas.push(vec![variable(), variable()]); 91 | // eta.push(variable()); 92 | // } 93 | // } 94 | -------------------------------------------------------------------------------- /mantis/node/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod cows; 2 | mod cvms; 3 | -------------------------------------------------------------------------------- /mantis/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "blackbox" 3 | version = "0.0.3" 4 | description = "Python server which solves problems from provided input and additional data obtained from chains/indexers and outputs single route solution." 5 | authors = [] 6 | package-mode = true 7 | packages = [ 8 | { include = "simulation" }, 9 | { include = "blackbox" }, 10 | 11 | # { include = "cvm_runtime", from = "blackbox" }, 12 | # { include = "main.py", from = "blackbox" }, 13 | ] 14 | 15 | [tool.poetry.dependencies] 16 | python = ">=3.11.6, <=3.11.8" 17 | 18 | #fastapi-cache2 = "^0.2.1" # no needed now, too many things 19 | bech32 = "1.2.0" 20 | cosmpy = { version = "0.9.1" } 21 | 22 | #GLOP, PDLP require ortools which i fail to install 23 | 24 | # cvxpy = { version = "1.3.2", extras = [ 25 | # # "MOSEK", 26 | # "CBC", 27 | # # "CVXOPT", 28 | # # "GUROBI", 29 | # "SCIP", 30 | # # "CLARABEL", 31 | # # "GLPK", 32 | # # "XPRESS", 33 | # ]} 34 | ecdsa = "0.18.0" 35 | fastapi = { extras = ["all"], version = "0.108.0" } 36 | google-cloud = "0.34.0" 37 | googleapis-common-protos = "1.61.0" 38 | grpcio = "==1.51.1" 39 | jsonschema = "4.20.0" 40 | # maturin = "1.4.0" 41 | numpy = "1.23.4" 42 | pandas = { extras = [], version = "^2.1.4" } 43 | pendulum = "==2.0.3" 44 | 45 | protobuf = "4.25.1" 46 | pycryptodome = "3.19.1" 47 | pydantic = "2.6.2" 48 | pydantic_settings = "2.1.0" 49 | python-dateutil = "2.8.2" 50 | requests = "2.31.0" 51 | rpds-py = "0.13.2" 52 | scipy = "1.9.3" 53 | pydantic-extra-types = "2.4.1" 54 | uvicorn = "0.25.0" 55 | methodtools = "0.4.7" 56 | tqdm = "^4.66.0" 57 | orjson="3.9.10" 58 | typeguard = "2.13.3" 59 | 60 | deepdiff = "*" 61 | # PySCIPOpt = "5.0.0" # i set it directly to allow override SCIP dep (currently it is goes as binary with this wheel)s 62 | # on if used only: 63 | # clarabel = "0.6.0" 64 | # cylp = "0.92.2" # have problems installing 65 | # ortools="9.4.1874" # have problems installing 66 | 67 | disjoint-set = "0.7.4" 68 | anytree = "2.12.1" 69 | shelved-cache = "^0.3.1" 70 | cachetools = "^5.3.2" 71 | loguru = "^0.7.2" 72 | mpire = "^2.10.0" 73 | 74 | [tool.poetry.dev-dependencies] 75 | pytest = "^7.4.3" 76 | # ruff = "0.1.15" 77 | pyright = "^1.1.353" 78 | 79 | 80 | [build-system] 81 | requires = ["poetry-core"] 82 | build-backend = "poetry.core.masonry.api" 83 | 84 | [tool.poetry.scripts] 85 | blackbox = 'blackbox.main:start' 86 | 87 | [tool.ruff] 88 | unsafe-fixes = true 89 | respect-gitignore = true 90 | line-length = 120 91 | indent-width = 4 92 | 93 | [tool.ruff.lint] 94 | select = ["E", "F", "W", "Q", "I"] 95 | ignore = ["E203", "E501"] 96 | 97 | 98 | fixable = ["ALL"] 99 | unfixable = [] 100 | # use Unition and Optional for types, not | 101 | 102 | 103 | [tool.ruff.format] 104 | quote-style = "double" 105 | indent-style = "space" 106 | skip-magic-trailing-comma = false 107 | line-ending = "auto" 108 | docstring-code-format = true 109 | docstring-code-line-length = "dynamic" 110 | 111 | 112 | [tool.pyright] 113 | useLibraryCodeForTypes = true 114 | pythonVersion = "3.11" 115 | typeCheckingMode = "basic" 116 | verboseOutput = true 117 | pythonPlatform = "Linux" 118 | defineConstant = { DEBUG = true } -------------------------------------------------------------------------------- /mantis/simulation/.style.yapf: -------------------------------------------------------------------------------- 1 | # python3 -m pip install yapf 2 | # python3 -m yapf -pir . 3 | 4 | [style] 5 | based_on_style = google 6 | indent_width = 4 7 | column_limit = 80 8 | -------------------------------------------------------------------------------- /mantis/simulation/README.md: -------------------------------------------------------------------------------- 1 | # How it works 2 | 3 | 1. Solver solves Coincidence of Wants orders 4 | 2. Remaining amount solved, if possible, via cross chain routes. -------------------------------------------------------------------------------- /mantis/simulation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/simulation/__init__.py -------------------------------------------------------------------------------- /mantis/simulation/orders/README.md: -------------------------------------------------------------------------------- 1 | # 2-assets-matching 2 | 3 | 4 | Implementation of a Frequent batch auction with two assets. 5 | Three different solvers are implemented. 6 | A solver that maximizes arbitrage, an (altruistic) solver that maximizes volume, and a solver that has a target price willing to trade (we can think of he is able to arbitrage it in an off-chain market maker). 7 | -------------------------------------------------------------------------------- /mantis/simulation/orders/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/simulation/orders/__init__.py -------------------------------------------------------------------------------- /mantis/simulation/orders/matching.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal, getcontext 2 | 3 | from loguru import logger 4 | from objects import ( 5 | CFMM, 6 | CFMMProfitSolver, 7 | CFMMVolumeSolver, 8 | Order, 9 | OrderList, 10 | OrderType, 11 | Solution, 12 | Solver, 13 | ) 14 | 15 | 16 | def simulate(): 17 | getcontext().prec = 30 18 | 19 | # simple case of perfect matching 20 | orders = OrderList( 21 | [ 22 | Order(100000.0, 100000.0 / 3.0, OrderType.BUY), 23 | Order(3.0, 100000.0 / 3.0, OrderType.SELL), 24 | ] 25 | ) 26 | assert orders.value[0].is_acceptable_price(orders.value[1].limit_price) 27 | assert orders.value[1].is_acceptable_price(orders.value[0].limit_price) 28 | 29 | Solution.match_orders(orders, orders.compute_optimal_price()).print() 30 | 31 | # CoW part 32 | orders = OrderList([Order.random(std=0.1, mean=2) for _ in range(100)]) 33 | 34 | orders.print() 35 | 36 | solution = Solution.match_orders(orders, Decimal("1")) 37 | solution.print() 38 | 39 | solution_2 = Solution.match_orders(orders, Decimal("2.05")) 40 | solution_2.print() 41 | Solution.match_orders(orders, orders.compute_optimal_price()).print() 42 | 43 | Solution.random(num_orders=300, volume_range=(100, 200), mean=1, std=0.01) 44 | 45 | tokens = int(1e5) 46 | solver = Solver(orders, Decimal(1.1), tokens, 1000) 47 | 48 | ob: Solution = solver.solve() 49 | ob.print() 50 | 51 | # solve with CFMM 52 | cfmm = CFMM(Decimal("1e6"), Decimal("0.95e6"), fee=Decimal("0")) 53 | 54 | volume_solver: Solver = CFMMVolumeSolver(orders, cfmm, tokens, tokens) 55 | v_ob = volume_solver.solve() 56 | v_ob.print() 57 | 58 | profit_solver: Solver = CFMMProfitSolver(ob.orders, cfmm, tokens, tokens) 59 | p_ob = profit_solver.solve() 60 | ob.print() 61 | 62 | logger.info(f"Volume of volume solver: {v_ob.match_volume:.2f} and Profit solver: {p_ob.match_volume:.2f}") 63 | logger.info( 64 | f"PROFIT volume_solver: {volume_solver.profit(volume_solver.order):.2f} profit_solver: {profit_solver.profit(profit_solver.order):.2f}" 65 | ) 66 | 67 | 68 | simulate() 69 | -------------------------------------------------------------------------------- /mantis/simulation/orders/types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class OrderType(Enum.Enum): 5 | BUY = "Buy" 6 | SELL = "Sell" 7 | 8 | def __str__(self) -> str: 9 | return self.value 10 | -------------------------------------------------------------------------------- /mantis/simulation/routers/README.md: -------------------------------------------------------------------------------- 1 | # How to contribute solver 2 | 3 | ## 0. Features 4 | 5 | Document what features solver supports: 6 | 7 | - loops 8 | - splits 9 | - N-1 10 | - 1-N 11 | - N-N 12 | - constant fees 13 | - asymmetric fees 14 | - non curved venues 15 | 16 | ## 1. Files 17 | 18 | Solver to be separate file. 19 | There should be test prefixed python file in same folder as solver simulating and/or testing case(s) of data input 20 | 21 | ## 2. Installable 22 | 23 | In case solver can be easy installed locally (needs registration to download and cannot distiribute downloaded code), 24 | NLP solvers has servers like NEOS, Gurobi, MOSEK and standard format to consume 25 | Any solver Python code used to call it to be installed via ../../pyproject.toml Poetry dependency 26 | 27 | ## 3. Proven 28 | 29 | There must be few tests and documentation. 30 | 31 | If there are a lot of simulation/tests, docs are less needed. 32 | 33 | In case of few tests, need to write next. 34 | 35 | Solver type(NLP, MILP, etc) to be described. 36 | Difference from convex optimal routing solver to be described. 37 | 38 | ## 4. Production 39 | 40 | Should use ../data.py as input/output. 41 | 42 | 43 | # How each router(solver) works 44 | 45 | Router is solver which interprets its output a route 46 | 47 | 1. prepares data from general input into what it can use, or rejects if cannot handle request 48 | 2. solves problem 49 | 3. "interprets" route from its solution, or reason why no route found 50 | 51 | 52 | ## How oracle work for OR solution 53 | 54 | -------------------------------------------------------------------------------- /mantis/simulation/routers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/simulation/routers/__init__.py -------------------------------------------------------------------------------- /mantis/simulation/routers/angeris_cvxpy/__init__._py: -------------------------------------------------------------------------------- 1 | from .algorithms import cvxpy_to_data # noqa 2 | from .data import CvxpySolution # noqa 3 | -------------------------------------------------------------------------------- /mantis/simulation/routers/data/assets_pairs_xyk.csv: -------------------------------------------------------------------------------- 1 | pool_id,in_asset_id,out_asset_id,fee_of_in_per_million,fee_of_out_per_million,weight_of_a,weight_of_b,pool_value_in_usd,in_token_amount,out_token_amount,metadata 2 | 1, 1,123,1000, 1000, 5000,5000, 10000000, 1000000000000, 1000000000000, "very normal pool" 3 | 2, 4,42,100, 1000, 1000,5000, 100000, 1234567, 666, "little bit assymetric pool" 4 | 300, 7,12,100,100, 100, 100, 5000, 10000000, 123, 5 | 100500, 7,12,100,100, 100, 100, 10000000, 123, 555, "Not all pools may have USD price" 6 | -------------------------------------------------------------------------------- /mantis/simulation/routers/data/assets_transfers.csv: -------------------------------------------------------------------------------- 1 | in_asset_id,out_asset_id,usd_fee_transfer,metadata,amount_of_in_token,amount_of_out_token,fee_per_million 2 | 1,2,100,"token id A to B with tranfer fee normalized to USD",10000000,1000000,1000 3 | 2,3,100,"",10000000,1000000.0,1000 4 | 3,2,100,"",10000000,1000000.0,1000 5 | 2,1,100,"there is B to C usually with same price",10000000,1000000,1000 6 | 5,6,1000,"",10000000,1000000.0,1000 7 | 6,7,2000,"",10000000,1000000.0,1000 8 | 7,6,2000,"",10000000,1000000.0,1000 9 | 6,5,1000, "sometimes back price is different",10000000,1000000,1000 10 | 8,9,2,"",10000000,1000000.0,1000 11 | 9,10,4,"",10000000,1000000.0,1000 12 | 3,666,0,"Not all assets may have USD transfer",10000000,1000000,1000 13 | 10,11,8,"",10000000,1000000.0,1000 14 | 11,12,16,"sometimes there is no B to A, only A to B",10000000,1000000,1000 15 | 42,123,0, "(in id <-> out id) is set id",10000000,1000000,1000 -------------------------------------------------------------------------------- /mantis/simulation/routers/errors.py: -------------------------------------------------------------------------------- 1 | class Infeasible(Exception): 2 | """ 3 | Cannot find path. Does not mean it does not exist. 4 | """ 5 | 6 | pass 7 | -------------------------------------------------------------------------------- /mantis/simulation/routers/oracles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/simulation/routers/oracles/__init__.py -------------------------------------------------------------------------------- /mantis/simulation/routers/oracles/usdoracle.py: -------------------------------------------------------------------------------- 1 | # oracles which tell price via connections and possible amounts available to go over venues 2 | import copy 3 | from typing import TypeVar, Union 4 | 5 | from disjoint_set import DisjointSet 6 | 7 | TId = TypeVar("TId", int, str) 8 | TNetworkId = TypeVar("TNetworkId", int, str) 9 | TAmount = TypeVar("TAmount", int, str) 10 | 11 | 12 | def merge_by_connection_from_existing( 13 | oracles: dict[TId, Union[float, None] | None], 14 | transfers: list[tuple[TId, TId]], 15 | ) -> dict[TId, Union[float, None] | None]: 16 | """ 17 | given set of data about assets, find very approximate ratio of one asset to other 18 | Very fast one and super sloppy on, 19 | considers if there is connection in general, 20 | same price and all price everywhere. 21 | No penalty for high fees/long route and non equilibrium. 22 | """ 23 | 24 | if not oracles or len(oracles) == 0: 25 | oracles = {} 26 | oracles = copy.deepcopy(oracles) 27 | ds = DisjointSet() 28 | all_asset_ids = set() 29 | for t in transfers: 30 | ds.union(t[0], t[1]) 31 | all_asset_ids.add(t[0]) 32 | all_asset_ids.add(t[1]) 33 | for asset_id in oracles.keys(): 34 | all_asset_ids.add(asset_id) 35 | for asset_id in all_asset_ids: 36 | if asset_id not in oracles or oracles[asset_id] is None or oracles[asset_id] == 0: 37 | for other_id, value in oracles.items(): 38 | if value and ds.connected(asset_id, other_id) and value > 0: 39 | oracles[asset_id] = value 40 | break 41 | return oracles 42 | 43 | 44 | def test(): 45 | oracles = { 46 | 1: 1.0, 47 | 2: None, 48 | 3: None, 49 | 4: 2.0, 50 | } 51 | transfers = [ 52 | (2, 4), 53 | (1, 2), 54 | ] 55 | 56 | merge_by_connection_from_existing(oracles, transfers) 57 | assert oracles[2] == 2.0 58 | assert oracles[1] == 1.0 59 | assert oracles[3] is None 60 | assert oracles[4] == 2.0 61 | -------------------------------------------------------------------------------- /mantis/simulation/routers/test_data_types.py: -------------------------------------------------------------------------------- 1 | # for alignment on input and output of algorithm 2 | 3 | from mantis.simulation.routers.data import ( 4 | Exchange, 5 | SingleInputAssetCvmRoute, 6 | Spawn, 7 | ) 8 | 9 | 10 | def test_output_route_centauri_osmosis(): 11 | exchange = Exchange( 12 | in_asset_amount=100, 13 | pool_id=1, 14 | next=[], 15 | out_asset_amount=42, 16 | out_asset_id=13, 17 | in_asset_id=144, 18 | ) 19 | 20 | spawn = Spawn( 21 | in_asset_amount=100, 22 | out_asset_id=1, 23 | in_asset_id=2, 24 | out_asset_amount=42, 25 | next=[exchange.model_dump()], 26 | ) 27 | SingleInputAssetCvmRoute(next=[spawn], in_amount=1000) 28 | -------------------------------------------------------------------------------- /mantis/simulation/routers/test_production.py: -------------------------------------------------------------------------------- 1 | from mantis.simulation.routers.angeris_cvxpy import cvxpy_to_data 2 | from mantis.simulation.routers.generic_linear import route 3 | from simulation.routers.data import Ctx, new_data, new_input, new_pair 4 | 5 | 6 | def test_1(): 7 | pool_2376844893874674201515870126122 = new_pair( 8 | 2376844893874674201515870126122, 9 | 237684487542793012780631851015, 10 | 237684487542793012780631851016, 11 | 10, 12 | 10, 13 | 1, 14 | 1, 15 | 4192544, 16 | 1000000, 17 | 1000000, 18 | ) 19 | pool_2376844893874674201515870126124 = new_pair( 20 | 2376844893874674201515870126124, 21 | 237684487542793012780631851010, 22 | 237684487542793012780631851015, 23 | 10, 24 | 10, 25 | 1, 26 | 1, 27 | 271173, 28 | 151671, 29 | 42549, 30 | ) 31 | 32 | data = new_data([pool_2376844893874674201515870126122, pool_2376844893874674201515870126124], []) 33 | ctx = Ctx() 34 | input = new_input(237684487542793012780631851015, 237684487542793012780631851016, 446, 1) 35 | result = route(input, data) 36 | cvxpy_to_data(input, data, ctx, result) 37 | -------------------------------------------------------------------------------- /mantis/simulation/routers/venue.py: -------------------------------------------------------------------------------- 1 | # adapter class for venue formulas and approximation, like UniV2,3,4; Balancer, OrderBook convex approximation 2 | 3 | 4 | -------------------------------------------------------------------------------- /mantis/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc -------------------------------------------------------------------------------- /mantis/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Provision Solver 2 | 3 | ## Prerequisites 4 | 5 | - Digital Ocean account 6 | - [Terraform v1.4.2+](https://developer.hashicorp.com/terraform/install) 7 | 8 | ## Digital Ocean 9 | 10 | Generate PAT in Digital Ocean's console panel: 11 | - In the left menu, click API, which takes you to the Applications & API page on the Tokens tab. In the Personal access tokens section, click the Generate New Token button. 12 | 13 | [How to Create a Personal Access Token](https://docs.digitalocean.com/reference/api/create-personal-access-token/) 14 | 15 | export your Digital Ocean's Personal Access token 16 | ```bash 17 | export DO_PAT="your_personal_access_token" 18 | ``` 19 | 20 | ### Provision droplet 21 | 22 | Use bash wrapper to provision droplet 23 | ```bash 24 | ./provision.sh 25 | ``` 26 | 27 | At the end of provisioning script you wil find some info about your deployment: 28 | ``` 29 | Apply complete! Resources: 3 added, 1 changed, 1 destroyed. 30 | 31 | Outputs: 32 | 33 | droplet_id = "406459888" 34 | ipv4_address = "134.122.118.208" 35 | ipv4_address_private = "10.116.0.3" 36 | name = "ubuntu-nyc1-node-01" 37 | region = "nyc1" 38 | size = "s-1vcpu-1gb" 39 | tags = toset([ 40 | "mantis", 41 | "solver", 42 | ]) 43 | ``` 44 | 45 | Not `ipv4_address` to access droplet in next step. 46 | 47 | ### Access droplet 48 | 49 | To access droplet, use your keyfile 50 | ```bash 51 | ssh -i path_to_prv_file root@ 52 | ``` 53 | 54 | ```bash 55 | ssh -i ~/.ssh/do_test_id_rsa root@134.122.118.208 56 | ``` 57 | 58 | Together with provisioning of droplet, following tools are installed and setup via `cloudinit`: 59 | 60 | ```bash 61 | composable@ubuntu-nyc1-node-01:~$ python --version 62 | Python 3.11.7 63 | ``` 64 | 65 | ```bash 66 | composable@ubuntu-nyc1-node-01:~$ poetry --version 67 | Poetry (version 1.8.2) 68 | ``` 69 | 70 | ### Run solver 71 | 72 | In order to run solver, ssh into droplet and start project via projectry command: 73 | 74 | - switch to `composable` user 75 | ```bash 76 | su - composable 77 | ``` 78 | 79 | - run poetry commands 80 | ```bash 81 | cd composable-vm/mantis/ 82 | poetry install 83 | poetry run blackbox 84 | ``` 85 | 86 | at the end you will see: 87 | ``` 88 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) 89 | INFO: Started reloader process [41097] using WatchFiles 90 | INFO: Started server process [41103] 91 | INFO: Waiting for application startup. 92 | TRACE: ASGI [1] Started scope={'type': 'lifespan', 'asgi': {'version': '3.0', 'spec_version': '2.0'}, 'state': {}} 93 | TRACE: ASGI [1] Receive {'type': 'lifespan.startup'} 94 | TRACE: ASGI [1] Send {'type': 'lifespan.startup.complete'} 95 | INFO: Application startup complete. 96 | ``` 97 | 98 | 99 | Visit: http://:8000/docs 100 | 101 | You should see something like: 102 | 103 | ![mantis docs](./docs.png) 104 | 105 | 106 | ### Destroy infrastructure 107 | 108 | If you don't need that infrastructure anymore, you can use: 109 | ```bash 110 | ./destroy.sh 111 | ``` 112 | to decommision all components of infrasture. -------------------------------------------------------------------------------- /mantis/terraform/destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | terraform destroy \ 4 | -var "do_token=${DO_PAT}" -------------------------------------------------------------------------------- /mantis/terraform/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComposableFi/composable-vm/5773384150895cbcc9c94ff045ae9ffec5cf1642/mantis/terraform/docs.png -------------------------------------------------------------------------------- /mantis/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "digitalocean_droplet" "droplet" { 2 | name = "ubuntu-nyc1-node-01" 3 | image = "ubuntu-22-04-x64" 4 | region = "nyc1" 5 | size = "s-1vcpu-1gb" 6 | ssh_keys = [ 7 | digitalocean_ssh_key.sshkey.id 8 | ] 9 | tags = [digitalocean_tag.tag1.id, digitalocean_tag.tag2.id] 10 | 11 | user_data = file("mantis_app.yaml") 12 | } 13 | 14 | resource "digitalocean_tag" "tag1" { 15 | name = "mantis" 16 | } 17 | 18 | resource "digitalocean_tag" "tag2" { 19 | name = "solver" 20 | } 21 | 22 | 23 | resource "digitalocean_firewall" "firewall" { 24 | name = "only-22-8080" 25 | 26 | droplet_ids = [digitalocean_droplet.droplet.id] 27 | 28 | # open ssh 29 | inbound_rule { 30 | protocol = "tcp" 31 | port_range = "22" 32 | source_addresses = ["0.0.0.0/0", "::/0"] 33 | } 34 | 35 | # open 8000 36 | inbound_rule { 37 | protocol = "tcp" 38 | port_range = "8000" 39 | source_addresses = ["0.0.0.0/0", "::/0"] 40 | } 41 | 42 | # open all outgoing tcp 43 | outbound_rule { 44 | protocol = "tcp" 45 | port_range = "1-65535" 46 | destination_addresses = ["0.0.0.0/0", "::/0"] 47 | } 48 | 49 | # open all outgoing udp 50 | outbound_rule { 51 | protocol = "udp" 52 | port_range = "1-65535" 53 | destination_addresses = ["0.0.0.0/0", "::/0"] 54 | } 55 | 56 | # open all outgoing icmp 57 | outbound_rule { 58 | protocol = "icmp" 59 | destination_addresses = ["0.0.0.0/0", "::/0"] 60 | } 61 | } 62 | 63 | 64 | resource "digitalocean_ssh_key" "sshkey" { 65 | name = "my ssh public key" 66 | public_key = var.ssh_public_key 67 | } 68 | 69 | resource "digitalocean_project" "project" { 70 | name = "solver" 71 | description = "Mantis solver" 72 | purpose = "Solver related resources" 73 | environment = "development" 74 | resources = [ 75 | "${digitalocean_droplet.droplet.urn}" 76 | ] 77 | } -------------------------------------------------------------------------------- /mantis/terraform/mantis_app.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | groups: 3 | - ubuntu: [root,sys] 4 | - composable 5 | 6 | # Add users to the system. Users are added after groups are added. 7 | users: 8 | - default 9 | - name: composable 10 | gecos: composable 11 | shell: /bin/bash 12 | primary_group: composable 13 | sudo: ALL=(ALL) NOPASSWD:ALL 14 | groups: users, admin 15 | lock_passwd: false 16 | 17 | package_update: true 18 | package_upgrade: true 19 | 20 | packages: 21 | - python3-pip 22 | - libedit-dev 23 | - libncurses5-dev 24 | - zlib1g 25 | - zlib1g-dev 26 | - libssl-dev 27 | - libbz2-dev 28 | - libsqlite3-dev 29 | - libffi-dev 30 | - libssl-dev 31 | - liblzma-dev 32 | - libreadline-dev 33 | 34 | write_files: 35 | - path: /run/scripts/install.sh 36 | content: | 37 | #!/bin/bash 38 | 39 | # install pyenv 40 | curl https://pyenv.run | bash 41 | 42 | cat >> $HOME/.bashrc << 'EOF' 43 | export PYENV_ROOT="$HOME/.pyenv" 44 | [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" 45 | eval "$(pyenv init -)" 46 | EOF 47 | 48 | cat >> $HOME/.profile << 'EOF' 49 | export PYENV_ROOT="$HOME/.pyenv" 50 | [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" 51 | eval "$(pyenv init -)" 52 | EOF 53 | 54 | source $HOME/.profile 55 | 56 | # install python version 57 | pyenv install 3.11.7 58 | pyenv global 3.11.7 59 | 60 | # install peotry 61 | curl -sSL https://install.python-poetry.org | python3 - 62 | 63 | cat >> $HOME/.profile << 'EOF' 64 | export PATH="/home/composable/.local/bin:$PATH" 65 | EOF 66 | 67 | source $HOME/.profile 68 | 69 | # clone repo 70 | git clone https://github.com/ComposableFi/composable-vm.git $HOME/composable-vm 71 | permissions: '0755' 72 | 73 | runcmd: 74 | - [ su, "composable", "/run/scripts/install.sh" ] 75 | 76 | -------------------------------------------------------------------------------- /mantis/terraform/output.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | droplet_id = "${digitalocean_droplet.droplet.id}" 3 | droplet_ipv4_address = "${digitalocean_droplet.droplet.ipv4_address}" 4 | droplet_ipv4_address_private = "${digitalocean_droplet.droplet.ipv4_address_private}" 5 | droplet_ipv6_address = "${digitalocean_droplet.droplet.ipv6_address}" 6 | droplet_region = "${digitalocean_droplet.droplet.region}" 7 | droplet_name = "${digitalocean_droplet.droplet.name}" 8 | droplet_size = "${digitalocean_droplet.droplet.size}" 9 | droplet_image = "${digitalocean_droplet.droplet.image}" 10 | droplet_tags = "${digitalocean_droplet.droplet.tags}" 11 | } 12 | 13 | output "droplet_id" { 14 | description = "List of IDs of Droplets" 15 | value = local.droplet_id 16 | } 17 | 18 | output "name" { 19 | description = "List of names of Droplets" 20 | value = local.droplet_name 21 | } 22 | 23 | output "ipv4_address" { 24 | description = "List of public IPv4 addresses assigned to the Droplets" 25 | value = local.droplet_ipv4_address 26 | } 27 | 28 | output "ipv4_address_private" { 29 | description = "List of private IPv4 addresses assigned to the Droplets, if applicable" 30 | value = local.droplet_ipv4_address_private 31 | } 32 | 33 | output "region" { 34 | description = "List of regions of Droplets" 35 | value = local.droplet_region 36 | } 37 | 38 | output "size" { 39 | description = "List of sizes of Droplets" 40 | value = local.droplet_size 41 | } 42 | 43 | output "tags" { 44 | description = "List of tags of Droplets" 45 | value = local.droplet_tags 46 | } -------------------------------------------------------------------------------- /mantis/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | digitalocean = { 4 | source = "digitalocean/digitalocean" 5 | version = "~> 2.0" 6 | } 7 | } 8 | } 9 | 10 | # Configure the DigitalOcean Provider 11 | provider "digitalocean" { 12 | token = var.do_token 13 | } -------------------------------------------------------------------------------- /mantis/terraform/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | terraform init 4 | 5 | terraform plan \ 6 | -var "do_token=${DO_PAT}" \ 7 | -var-file="terraform.tfvars" 8 | 9 | terraform apply \ 10 | -var "do_token=${DO_PAT}" \ 11 | -var-file="terraform.tfvars" -------------------------------------------------------------------------------- /mantis/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | # Set the variable value in *.tfvars file 2 | # or using -var="do_token=..." CLI option 3 | variable "do_token" {} 4 | 5 | variable "ssh_public_key" {} -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-02-03" 3 | components = [ 4 | "clippy", 5 | "llvm-tools", 6 | "rust-analyzer", 7 | "rustfmt", 8 | "rust-src", 9 | ] 10 | targets = [ 11 | "wasm32-unknown-unknown", 12 | "thumbv7em-none-eabi", 13 | ] 14 | -------------------------------------------------------------------------------- /schema/raw/instantiate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "allOf": [ 5 | { 6 | "$ref": "#/definitions/HereItem" 7 | } 8 | ], 9 | "definitions": { 10 | "Addr": { 11 | "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", 12 | "type": "string" 13 | }, 14 | "HereItem": { 15 | "type": "object", 16 | "required": [ 17 | "admin", 18 | "network_id" 19 | ], 20 | "properties": { 21 | "admin": { 22 | "description": "The admin which is allowed to update the bridge list.", 23 | "allOf": [ 24 | { 25 | "$ref": "#/definitions/Addr" 26 | } 27 | ] 28 | }, 29 | "network_id": { 30 | "description": "Network ID of this network where contract is deployed", 31 | "allOf": [ 32 | { 33 | "$ref": "#/definitions/NetworkId" 34 | } 35 | ] 36 | } 37 | } 38 | }, 39 | "NetworkId": { 40 | "description": "Newtype for CVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.", 41 | "type": "integer", 42 | "format": "uint32", 43 | "minimum": 0.0 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /schema/raw/response_to_get_all_asset_venues.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Array_of_AssetsVenueItem", 4 | "type": "array", 5 | "items": { 6 | "$ref": "#/definitions/AssetsVenueItem" 7 | }, 8 | "definitions": { 9 | "AssetId": { 10 | "description": "Newtype for CVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the CVM protocol and no assumption must be made on how it is computed.", 11 | "type": "string" 12 | }, 13 | "AssetsVenueItem": { 14 | "description": "assets which can be transomed into each other via venue", 15 | "type": "object", 16 | "required": [ 17 | "from_asset_id", 18 | "to_asset_id", 19 | "venue_id" 20 | ], 21 | "properties": { 22 | "from_asset_id": { 23 | "$ref": "#/definitions/AssetId" 24 | }, 25 | "to_asset_id": { 26 | "$ref": "#/definitions/AssetId" 27 | }, 28 | "venue_id": { 29 | "$ref": "#/definitions/VenueId" 30 | } 31 | } 32 | }, 33 | "Displayed_for_uint128": { 34 | "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n```rust use cvm::shared::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", 35 | "type": "string" 36 | }, 37 | "VenueId": { 38 | "oneOf": [ 39 | { 40 | "type": "string", 41 | "enum": [ 42 | "transfer" 43 | ] 44 | }, 45 | { 46 | "type": "object", 47 | "required": [ 48 | "exchange" 49 | ], 50 | "properties": { 51 | "exchange": { 52 | "$ref": "#/definitions/Displayed_for_uint128" 53 | } 54 | }, 55 | "additionalProperties": false 56 | } 57 | ] 58 | } 59 | } 60 | } 61 | --------------------------------------------------------------------------------